java 垃圾回收機制以及經典垃圾回收器詳解
判斷對象存活方法
引用計數法:在對象中添加一個引用計數子,每當一個地方引用他時,計數器就加一,當引用失效時,計數器就減一。
會有對象循環引用問題:
objA.instance = objB objB.instance = objA
objA 有objB 的引用 objB 有 objA 的引用,他們相互引用著對方。導致他們無法回收。
可達性分析:
從GC Roots 根對象作為起點,根據引用關系向下搜索,如果對象可達,就說明對象存活,如果對象不可達,就說明對象可以被回收。
GC Roots的根對象為:
1)在虛擬機棧 棧幀中的 本地變量表 中引用的對象
2)方法區靜態屬性引用的對象
3)方法區常量引用 的對象,如字符串常量池中的引用
4)本地方法棧中JNI引用的對象
5)虛擬機內部引用的對象,如基本數據類型對應的Class對象,一些常駐的異常對象等,還有系統類加載器
6)被同步鎖持有的對象
等
收集線程和用戶線程在並發可達性分析
並發 的可達性分析,由於用戶線程會即時修改對象的引用關系, 可能會造成兩種異常:
1)原本消亡的對象錯誤標記為存活,這個可以接受,就造成浮動垃圾,下一次收集即可。
2)原本存活的對象 標記為 消失。
三色法分析圖:
黑色:已經掃描過的對象
灰色:已經訪問過,但還有一個引用沒有被掃描
白色:為被訪問過,若到最後還是白色,說明此對象是需要回收的。
黑色誤標記為白色有兩個條件
1)復制器插入一條或多條從黑色對象到白色對象的新引用
2)復制器刪除瞭全部從灰色對象到該白色對象的直接或間接的引用
解決方法:
1)增量更新,破壞條件1,把黑色對象對白色對象的新增引用記錄下來,等並發掃描結束後,再以這些對象出發重新掃描。
2)原始快照(SATB),破解條件2,當灰色對象要刪除對白色對象的引用關系時,記錄下來。並發結束後再以記錄節點開始重新掃描。
分代收集
堆:
新生代(1/3) 老年代(2/3)
新生代 分為 Eden/From/To
新生代存放:比較小,時長比較小
老年代:比較大 存放時長比較大
輕GC
重GC(full GC) -> STW(停止事件),fallGc特別費資源
Eden -> from <-> To -> old
對象在from和to循環15(默認)次之後,會放到老年代
垃圾收集算法
標記-清除 算法:
算法分為標記 和 清除兩個階段,首先標記出所需要回收的對象,標記完成之後,統一回收所標記的對象。
優點:最基礎的算法,實現簡單
缺點:1)執行效率不穩定,對象越多,效率越低
2)內存碎片化,需要分配大對象時可能無足夠連續的空間。
代表垃圾收集器:
標記復制算法(復制算法):
把內存分為大小相等的兩塊,每次隻使用其中一款,當一塊快用完時,它將存活的對象復制到另一塊上。
優點:能產生連續的空間
缺點:耗內存,對象存活率較高時,效率會降低(不適合老年代)。
代表垃圾收集器: 很多新生代的回收,都用這種算法。
標記整理 算法
首先標記出所需要的對象,標記完成後,對存活對象移動到內存的一段,然後清除邊界外的對象。
優點:有連續的內存空間;系統吞吐量(用戶線程和收集器的效率總和)會提高。
缺點:整理內存耗時會比較大,會造成 “Stop The World”;
代表垃圾收集器:Parallel Scavenge收集器
CMS中主要用標記清除算法,但是當內存碎片化到影響對象分配時,就會使用一次標記整理算法 去整理內存碎片。
垃圾收集器
Serial收集器
最基礎最悠久的垃圾收集器,叫做 串行收集器,它進行垃圾收集的時候,會停止用戶線程(Stop the world)。
新生代垃圾收集器用 Serial,基於復制算法
老年代垃圾收集器用 Serial Old,基於標記整理算法。
優點:所有垃圾收集器中內存消耗最小的,對單核或單線程處理器來說,效率很高。運行在客戶端
缺點:stop the world
ParNew收集器
ParNew收集器就是Serial收集器的多線程並行版,除瞭支持多線並行收集之外,沒有太多創新之處。
CMS垃圾收集器作為老年代垃圾收集器,不能與Parallel Scavenge配合工作,隻能選擇 ParNew或者Serial收集器。
Parallel Scavenge收集器
新生代垃圾收集器,基於 標記復制算法,也是可以通過並行收集的多線程收集器。他關註 吞吐量(用戶線程時間/總時間)。
CMS 垃圾收集器
CMS 收集器是一種以獲取最短停頓時間作為目標的垃圾收集器。基於標記清除算法。
步驟:
初始標記->並發標記->重新標記->並發清除
1)初始標記:stop the world,標記GC ROOT 能直接關聯的對象,速度很快
2)並發標記:從GC Root直接關聯的對象開始遍歷整個對象圖,時間較長,但與用戶線程並行
3)重新標記:stop the world,修正並發標記期間對象的狀態的改變(增量更新算法,標記新的黑色指向白色的引用),時間也比較短。
4)並發清除:與用戶線程同步。
優點:並發收集、低停頓。
缺點:
1)對處理器資源敏感,當處理器核心數量在四個以下時,CMS對用戶程序影響很大。
2)有浮動垃圾,並發標記時會產生新的垃圾,但是CMS本次不會清理它,要等到下一次才會清理。從而可能造成內存不夠而產生Stop the world 的Full GC
3)基於標記清除算法,會產生大量的空間碎片,而觸發Full GC
G1垃圾收集器
關註吞吐量和延遲時間的最佳平衡。
從整體看,主要采用標記整理算法,從局部看,是標記-復制算法(兩個Region 之間的復制)。
G1把 java對 劃分為多個大小相等獨立區域 Region,每一個Region 都可以根據需要,扮演 Eden空間、Survivor空間或者老年代空間。
G1對年代的劃分存在概念上,它可以不是連續的區間。。
Region 還有一類特殊的Humongous區域,專門存儲大對象(1M-32M,可配),把超過Region大小的對象分配在連續的 Humongous Region之中。
每次收集時以Region作為最小單元,G1收集器去根據Region裡面垃圾隊的價值大小,在後臺維護一個優先級列表,優先處理回收價值收益最大的那些Region(每次收集到的內存大小及回收時間的經驗值)。
G1至少耗費打印java堆容量的10%到20%來維持收集工作。
TAMS指針:在並發過程中保存新建的對象。
STAB:灰色引用對白色引用的刪除 記錄下來。
G1的步驟:
1)初始標記:stop the world,僅僅標記GC ROOTs 能直接關聯的對象,並且修改TAMS指針
2)並發標記:從GC ROOTS 直接關聯的對象出發,掃描對象圖(並還要處理SATB記錄下並時有變動的對象),時間長,但是與用戶線程並行,
3)最終標記:stop the world,短暫,處理遺留下來的少量SATB記錄
4)帥選回收:stop the world,負責更新Region的統計數據,對Region的回收價值和成本進行排序。根據用戶所期望的停頓時間(JVM參數可配)來制定回收計劃。可以自由選擇多個Region作為回收集,然後把存活的對象復制到空的Region中,然後清空回收集的Region。
除瞭並發標記外,其它都用停止用戶線程,目標不是單純的追求低延遲,而是延遲可控的情況下獲取最大的吞吐量(用戶線程時間/總時間,總時間為用戶線程時間+垃圾收集時間)。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。