本文介紹JVM垃圾回收算法的具體實現(xiàn),介紹各個術(shù)語扇调,并圖文并茂介紹具體的實現(xiàn)細(xì)節(jié)汪榔。
垃圾回收判定及回收過程如下:
1、可達(dá)性分析算法
通過GC Roots
的根對象作為起始節(jié)點集肃拜,從這些節(jié)點開始痴腌,根據(jù)引用關(guān)系向下搜索,如果對象沒有被引用鏈引用到燃领,則判斷對象為可回收對象士聪。
2、GC Roots
根節(jié)點枚舉要掃描的對象集猛蔽,如下:
- 虛擬機棧中引用的對象
- 方法區(qū)中類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- 本地方法棧中JNI(Native)引用的對象
- Java虛擬機內(nèi)部的引用
- 所有被同步鎖(synchronized)持有的對象
- 反應(yīng)Java虛擬機內(nèi)部情況的JMXBean剥悟、JVMTI中注冊的回調(diào)、本地代碼緩存等曼库。
3区岗、根節(jié)點枚舉
掃描GC Roots
集合查找引用鏈,這是Stop The World
的操作,即所有用戶線程均要暫停毁枯,這是非常耗時的慈缔。即使號稱停頓時間可控,或者(幾乎)不會發(fā)生停頓的CMS种玛、G1藐鹤、ZGC等收集器,枚舉根節(jié)點也是要暫停的赂韵。
可達(dá)性分析算法耗時最長的查找引用鏈的過程已經(jīng)可以做到與用戶線程一起并發(fā)娱节,但根節(jié)點枚舉始終還是必須在一個能保障一致性的快照中才得以進(jìn)行。
使用OopMap
來快速找到虛擬機棧的引用祭示。
4肄满、OopMap
知道棧的哪個位置存放著對象引用。
5、安全點
在OopMap
的協(xié)助下稠歉,HotSpot可以快速準(zhǔn)確地完成GC Roots
枚舉讥电。
執(zhí)行指令后,在特定位置記錄OopMap信息轧抗,這些位置成為安全點
恩敌。
- 搶先式中斷 - 馬上暫定所有用戶線程 - 現(xiàn)在沒有這么做的
- 主動式中斷 - 線程去主動輪詢一個標(biāo)記,發(fā)現(xiàn)為真的時候就在最近的安全點上主動中斷掛起横媚。- 輪詢操作精簡為了一條匯編指令 test
6纠炮、安全區(qū)域
在這里引用關(guān)系不會發(fā)生改變,垃圾回收時不會管這些線程灯蝴,代碼執(zhí)行到要出安全區(qū)域的時候才會判定是否需要暫停恢口。
7、記憶集
存在問題: 在部分區(qū)域收集中穷躁,當(dāng)存在跨代引用耕肩,例如老年代引用新生代,那么新生代就不能被回收问潭。G1的的分區(qū)域收集也是如此猿诸。
經(jīng)驗法則分代假說:
1、弱分代假說: 絕大多數(shù)對象都是朝生夕滅的狡忙。
2梳虽、強分代假說: 熬過越多此垃圾收集過程的對象就越難以消亡。
3灾茁、跨代引用假說: 跨代引用相對于同代引用來說僅占極少數(shù)窜觉。
由以上假說可推理出,如果有跨代引用北专,則新生代晉升到老年代后禀挫,這種跨代引用也隨即被消除了。所以可以不為了少量的跨代引用而掃描整個老年代拓颓,這種代價太昂貴语婴,所以出現(xiàn)了記憶集
。
記憶集把老年代劃分成若干個小塊录粱,標(biāo)識出哪一塊內(nèi)存存在跨代引用腻格,此后發(fā)生Minor GC的時候,只有這小部分才會被加入GC Roots進(jìn)行掃描啥繁。
8、卡表
卡表
是記憶集
的一種具體實現(xiàn)青抛,它定義了記憶集的記錄精度旗闽、與堆內(nèi)存的映射關(guān)系等。
有三精度
1、字長精度
2适室、對象精度
3嫡意、卡精度 - 每個記錄精確到一塊內(nèi)存區(qū)域,該區(qū)域內(nèi)有對象含有跨代指針捣辆。
卡表最簡單的形式可以是一個字節(jié)數(shù)組蔬螟,可表示為CARD_TABLE[this address >> 9] = 0
,其中每一個元素是一個特定大小的內(nèi)存塊汽畴,成為卡頁
旧巾。
9、卡頁
hotSpot中每個卡頁
都是512字節(jié)忍些,每個卡頁包含不止一個對象鲁猩,只要有一個存在跨代指針,那么卡表的數(shù)組元素的值則為1罢坝,成為變臟廓握,否則為0。垃圾回收時嘁酿,篩選出變臟的元素隙券,加入到GC Roots一起進(jìn)行掃描。
10闹司、寫屏障
維護(hù)卡表狀態(tài)
寫屏障可以看作在虛擬機層面對"引用類型字段賦值"這個動作的AOP切面是尔,在引用對象賦值時會產(chǎn)生一個環(huán)形通知,供程序執(zhí)行額外的動作
G1之前都是寫后屏障开仰,在執(zhí)行引用字段賦值后拟枚,會更新卡表狀態(tài)。