[JAVA] JVM垃圾回收 - HotSpot的算法細(xì)節(jié)實(shí)現(xiàn)

本文介紹JVM垃圾回收算法的具體實(shí)現(xiàn),介紹各個(gè)術(shù)語(yǔ),并圖文并茂介紹具體的實(shí)現(xiàn)細(xì)節(jié)。
垃圾回收判定及回收過(guò)程如下:


1653579682.png

1、可達(dá)性分析算法

通過(guò)GC Roots的根對(duì)象作為起始節(jié)點(diǎn)集,從這些節(jié)點(diǎn)開(kāi)始,根據(jù)引用關(guān)系向下搜索,如果對(duì)象沒(méi)有被引用鏈引用到,則判斷對(duì)象為可回收對(duì)象。

2、GC Roots

根節(jié)點(diǎn)枚舉要掃描的對(duì)象集,如下:

  • 虛擬機(jī)棧中引用的對(duì)象
  • 方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象
  • 方法區(qū)中常量引用的對(duì)象
  • 本地方法棧中JNI(Native)引用的對(duì)象
  • Java虛擬機(jī)內(nèi)部的引用
  • 所有被同步鎖(synchronized)持有的對(duì)象
  • 反應(yīng)Java虛擬機(jī)內(nèi)部情況的JMXBean、JVMTI中注冊(cè)的回調(diào)、本地代碼緩存等。

3、根節(jié)點(diǎn)枚舉

掃描GC Roots集合查找引用鏈,這是Stop The World的操作,即所有用戶(hù)線程均要暫停,這是非常耗時(shí)的。即使號(hào)稱(chēng)停頓時(shí)間可控,或者(幾乎)不會(huì)發(fā)生停頓的CMS、G1、ZGC等收集器,枚舉根節(jié)點(diǎn)也是要暫停的。
可達(dá)性分析算法耗時(shí)最長(zhǎng)的查找引用鏈的過(guò)程已經(jīng)可以做到與用戶(hù)線程一起并發(fā),但根節(jié)點(diǎn)枚舉始終還是必須在一個(gè)能保障一致性的快照中才得以進(jìn)行。
使用OopMap來(lái)快速找到虛擬機(jī)棧的引用。

4、OopMap

知道棧的哪個(gè)位置存放著對(duì)象引用。

5、安全點(diǎn)

OopMap的協(xié)助下,HotSpot可以快速準(zhǔn)確地完成GC Roots枚舉。
執(zhí)行指令后,在特定位置記錄OopMap信息,這些位置成為安全點(diǎn)

  • 搶先式中斷 - 馬上暫定所有用戶(hù)線程 - 現(xiàn)在沒(méi)有這么做的
  • 主動(dòng)式中斷 - 線程去主動(dòng)輪詢(xún)一個(gè)標(biāo)記,發(fā)現(xiàn)為真的時(shí)候就在最近的安全點(diǎn)上主動(dòng)中斷掛起。- 輪詢(xún)操作精簡(jiǎn)為了一條匯編指令 test

6、安全區(qū)域

在這里引用關(guān)系不會(huì)發(fā)生改變,垃圾回收時(shí)不會(huì)管這些線程,代碼執(zhí)行到要出安全區(qū)域的時(shí)候才會(huì)判定是否需要暫停。

7、記憶集

存在問(wèn)題: 在部分區(qū)域收集中,當(dāng)存在跨代引用,例如老年代引用新生代,那么新生代就不能被回收。G1的的分區(qū)域收集也是如此。
經(jīng)驗(yàn)法則分代假說(shuō):
1、弱分代假說(shuō): 絕大多數(shù)對(duì)象都是朝生夕滅的。
2、強(qiáng)分代假說(shuō): 熬過(guò)越多此垃圾收集過(guò)程的對(duì)象就越難以消亡。
3、跨代引用假說(shuō): 跨代引用相對(duì)于同代引用來(lái)說(shuō)僅占極少數(shù)。
由以上假說(shuō)可推理出,如果有跨代引用,則新生代晉升到老年代后,這種跨代引用也隨即被消除了。所以可以不為了少量的跨代引用而掃描整個(gè)老年代,這種代價(jià)太昂貴,所以出現(xiàn)了記憶集。

記憶集把老年代劃分成若干個(gè)小塊,標(biāo)識(shí)出哪一塊內(nèi)存存在跨代引用,此后發(fā)生Minor GC的時(shí)候,只有這小部分才會(huì)被加入GC Roots進(jìn)行掃描。

8、卡表

卡表記憶集的一種具體實(shí)現(xiàn),它定義了記憶集的記錄精度、與堆內(nèi)存的映射關(guān)系等。

有三精度
1、字長(zhǎng)精度
2、對(duì)象精度
3、卡精度 - 每個(gè)記錄精確到一塊內(nèi)存區(qū)域,該區(qū)域內(nèi)有對(duì)象含有跨代指針。

卡表最簡(jiǎn)單的形式可以是一個(gè)字節(jié)數(shù)組,可表示為CARD_TABLE[this address >> 9] = 0,其中每一個(gè)元素是一個(gè)特定大小的內(nèi)存塊,成為卡頁(yè)。

9、卡頁(yè)

hotSpot中每個(gè)卡頁(yè)都是512字節(jié),每個(gè)卡頁(yè)包含不止一個(gè)對(duì)象,只要有一個(gè)存在跨代指針,那么卡表的數(shù)組元素的值則為1,成為變臟,否則為0。垃圾回收時(shí),篩選出變臟的元素,加入到GC Roots一起進(jìn)行掃描。

10、寫(xiě)屏障

維護(hù)卡表狀態(tài)

寫(xiě)屏障可以看作在虛擬機(jī)層面對(duì)"引用類(lèi)型字段賦值"這個(gè)動(dòng)作的AOP切面,在引用對(duì)象賦值時(shí)會(huì)產(chǎn)生一個(gè)環(huán)形通知,供程序執(zhí)行額外的動(dòng)作

G1之前都是寫(xiě)后屏障,在執(zhí)行引用字段賦值后,會(huì)更新卡表狀態(tài)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容