垃圾
沒有任何引用指向的對象都是垃圾
找垃圾的方法
- 引用計數法:一個對象創建時會分配一個計數器,有引用指向這個對象時,計數器+1,當引用失效,計數器-1,當計數器爲0時,就可以判斷爲一個垃圾對象(這種方法解決不了循環引用的問題)
- 根可達算法:以GC Roots爲根展開引用鏈路搜索,如果一個對象沒有任何鏈路跟GC Roots關聯,那麼就可以判斷爲一個垃圾對象
- GC Roots的對象:線程棧變量,靜態變量,常量池,JNI指針(本地方法對象)
垃圾清除的算法
- 標記清除算法:
- 優點:標記處垃圾對象後清除,算法相對簡單,存活對象多的情況效率較高
- 缺點:兩邊掃描(找出有用對象,找出垃圾對象並清理)效率偏低,容易產生碎片。
- 拷貝算法:
- 優點:適用於對象較少的情況,只掃描一次,效率提高沒有碎片
- 缺點:空間浪費,移動複製對象需要調整對象引用
- 標記壓縮算法:
- 優點:不會產生碎片,方便對象分配,不會內存減半
- 缺點:掃描兩次,需要移動對象,效率偏低。
堆區內存分步圖
進入老年代年齡的參數
-XX:MaxTenuringThreshold
棧上分配
- 線程私有小對象
- 無逃逸(沒有被外層引用)
- 支持標量替換(使用普通類型代替對象)
- 無需調整(不需要GC優化)
線程本地分配 TLAB(Thread Local Allocation Buffer)
- 佔用eden,默認1%
- 多線程的時候,不用競爭,eden就可以申請空間,提高效率
- 小對象
- 無需調整
對象何時進入老年代
超過tenuringThreShould 指定次數(YGC)
- Parallel Scavenge:15
- CMS:6
- G1:15
動態年齡
Eden和S1在GC中存活的對象,超過了S2容量的50%,年齡大的直接放入老年代
分配擔保
YGC期間,有對象進來,survivor區的空間不夠了,空間擔保直接進入老年代
對象創建內存分步流程圖
常見的垃圾回收器
Jdk誕生Serial追隨,提高效率誕生了PS,爲了配合CMS產生了ParNew,1.4後期誕生了CMS,CMS是里程碑式的GC,它開啓了併發回收的過程,但是CMS毛病較多,因此目前任何一個JDK版本默認是CMS 併發垃圾回收是因爲無法忍受STW
- Serial:年輕代串行回收
- Serial Old:單線程在老年代
- PS:年輕代,並行回收
- ParNew:年輕代配合CMS的並行回收
- CMS:
- 初始標誌:標記根對象
- 併發標記:佔失敗80%,一遍產生垃圾,一邊標記
- 重新標記:STW
- 併發清理:會產生浮動垃圾
缺點:內存碎片化,浮動垃圾 解決方案:降低觸發CMS閾值-XX:CMSinitiation Occupancy Fraction
92%減少到65%甚至更低,保證老年代有足夠的空間。