jvm 優(yōu)化篇-(8)-跨代引用問(wèn)題(RememberSet、CardTable、ModUnionTable、DirtyCard)

死神-露琪亞

1、什么是跨代引用?

紅色的線表示由虛擬機(jī)棧中發(fā)出的引用。顯然B--->A、E--->F都是跨代引用。


跨代引用邏輯圖

2、跨代引用對(duì)MonitorGC的影響

JVM GC 判斷對(duì)象是否可以回收使用可達(dá)性分析的方法,可達(dá)性分析首先需要找到 GC Roots 對(duì)象。

常規(guī)GC-Root:

  • 1)虛擬機(jī)棧(棧幀中的局部變量表)中引用的對(duì)象【Stack Local】。
  • 2)本地方法棧(native方法)引用的對(duì)象。
  • 3)方法區(qū)中類靜態(tài)屬性、靜態(tài)方法引用的對(duì)象。
  • 4)方法區(qū)中常量引用的對(duì)象。

MonitorGC個(gè)性GC-Root:

  • 5) RememberSet數(shù)據(jù)結(jié)構(gòu)(CardTable是具體實(shí)現(xiàn)類似數(shù)組)

這里重點(diǎn)講一下CardTable:
??????? 作用:采用空間換時(shí)間,不需要掃描整個(gè)Heap空間,降低MonitorGC耗時(shí)。跨代引用帶來(lái)的問(wèn)題,采用CardTable很好的規(guī)避了遍歷整個(gè)老年代的問(wèn)題。
??????? HotSpot JVM的卡頁(yè)(Card Page)大小為512字節(jié),卡表(Card Table)被實(shí)現(xiàn)為一個(gè)簡(jiǎn)單的字節(jié)數(shù)組,即卡表的每個(gè)標(biāo)記項(xiàng)為1個(gè)字節(jié)。

CardTable標(biāo)記著是否存在老年代引用新生代

如上圖發(fā)現(xiàn):B、C所在的Card Page在CardTable中被標(biāo)記上了。

【思考??】MonitorGC的GC-Root中包括CardTable,如何提高M(jìn)onitorGC效率?
就需要降低跨代引用的對(duì)象,盡量設(shè)置稍大些的From、To區(qū)域,盡量將對(duì)象消滅在Young Gen區(qū)域。同時(shí)也表示對(duì)象不是越早進(jìn)入老年代越好(老年代對(duì)象引用新生代對(duì)象就是一個(gè)很好的說(shuō)明)!!!。

3、跨代引用對(duì)CMS中 OldGC的影響

CMS-OldGC回收??分為7個(gè)步驟:


CMS的7個(gè)步驟
重點(diǎn)步驟解讀:
  • 1、初始標(biāo)記(Initial Mark)

    • 目標(biāo):進(jìn)行可達(dá)性分析,標(biāo)記GC ROOT能直接關(guān)聯(lián)到的對(duì)象。
    • 標(biāo)記范圍:Young Gen + Old Gen
    • 線程:JDK1.7是單線程,JDK1.8是多線程(-XX:+CMSParallelInitialMarkEnabled調(diào)整)
    • STW:觸發(fā)Stop-The-World
    • 特點(diǎn):速度極快
  • 2、并發(fā)標(biāo)記(Concurrent Mark)

    • 目標(biāo):遍歷階段1初始標(biāo)記出來(lái)的存活對(duì)象,然后繼續(xù)遞歸標(biāo)記這些對(duì)象可達(dá)的對(duì)象。(黑白灰三色標(biāo)記法)。
    • 標(biāo)記范圍:Young Gen + Old Gen
    • STW:不觸發(fā)
    • 特點(diǎn):慢,很耗時(shí)。
    • 特殊操作:通過(guò)Old區(qū)卡片標(biāo)記(Card Marking),提前把老年代空間邏輯劃分為相等大小的區(qū)域(Card Page),如果引用關(guān)系發(fā)生改變,JVM會(huì)將發(fā)生改變的區(qū)域標(biāo)記位“臟區(qū)”(Dirty Card)。JVM會(huì)對(duì)“并發(fā)標(biāo)記”階段應(yīng)用線程新產(chǎn)生的對(duì)象及對(duì)象涉及的引用修改做記錄(Mod-Union Table)
  • 3、預(yù)清理(Preclean)

    • 目標(biāo):2階段中記錄在Mod-Union Table的這些臟區(qū)會(huì)被找出來(lái),刷新引用關(guān)系,清除“臟區(qū)”標(biāo)記。
    • 標(biāo)記范圍: Old Gen。
    • 線程:GC線程和應(yīng)用線程也是并發(fā)執(zhí)行
    • STW:不觸發(fā)
    • 特點(diǎn):速度一般
  • 4、可中斷的預(yù)清理(Concurrent Abortable Preclean)

    • 目標(biāo):降低垃圾回收時(shí)對(duì)應(yīng)用的暫停時(shí)間,整個(gè)過(guò)程最耗時(shí)步驟在5(最終標(biāo)記),所以4步驟提前進(jìn)行。
    • 觸發(fā)條件:此階段在Eden區(qū)使用超過(guò)2M時(shí)啟動(dòng),當(dāng)然2M是默認(rèn)的閾值,可以通過(guò)參數(shù)修改。
    • 標(biāo)記范圍:Young Gen + Old Gen
      • 處理From、To區(qū)對(duì)象,標(biāo)記可達(dá)到老年代的對(duì)象。
      • 和3階段一樣,掃描處理Dirty Card中的對(duì)象。
    • 終止邏輯:CMS為了避免這個(gè)階段沒(méi)有等到Minor GC而陷入無(wú)限等待,提供了參數(shù)CMSMaxAbortablePrecleanTime ,默認(rèn)為5s,含義是如果可中斷的預(yù)清理執(zhí)行超過(guò)5s,不管發(fā)沒(méi)發(fā)生Minor GC,都會(huì)中止此階段,進(jìn)入Remark。
    • 線程:GC線程和應(yīng)用線程也是并發(fā)執(zhí)行。
    • STW:不觸發(fā)
    • 特點(diǎn):速度一般
    • 特殊:下一個(gè)階段要執(zhí)行最耗時(shí)且STW的Remark階段,Remark階段會(huì)將整個(gè)YoungGen作為GC-Root進(jìn)行操作,如果4階段能觸發(fā)一次MonitorGC就再好不過(guò)了。
  • 5、重新標(biāo)記(Final ReMark)

    • 目標(biāo):GC事件中第二次(也是最后一次)STW階段,目的是完成老年代中所有存活對(duì)象的標(biāo)記。
    • 標(biāo)記范圍:Young Gen + Old Gen
    • 線程:GC線程獨(dú)占執(zhí)行
    • STW:觸發(fā)Stop-The-World
    • 特點(diǎn):速度較慢,可以說(shuō)是整個(gè)CMS-Old GC的瓶頸點(diǎn)
    • 特殊:
      • 1、遍歷新生代對(duì)象,重新標(biāo)記
      • 2、根據(jù)GC Roots,重新標(biāo)記
      • 3、遍歷老年代的Dirty Card,重新標(biāo)記
  • 6、并發(fā)清除(Concurrent Sweep)

    • 目標(biāo):根據(jù)標(biāo)記結(jié)果清除垃圾對(duì)象。
    • 標(biāo)記范圍:Old Gen。
    • 線程:GC線程和應(yīng)用線程也是并發(fā)執(zhí)行
    • STW:不觸發(fā)
    • 特點(diǎn):速度一般。
  • 7、并發(fā)重置(Concurrent Reset)

    • 目標(biāo):重置CMS算法相關(guān)的內(nèi)部數(shù)據(jù), 為下一次GC循環(huán)做準(zhǔn)備。
    • 線程:GC線程和應(yīng)用線程也是并發(fā)執(zhí)行
    • STW:不觸發(fā)
    • 特點(diǎn):速度一般。
7步圖解:
三階段:初始標(biāo)記

二階段:并發(fā)標(biāo)記

三階段:并發(fā)預(yù)清理

六階段:并發(fā)清除

4、問(wèn)題思考??:

【思考??】CMS oldgc中remark耗時(shí)問(wèn)題,跨代引用是圖一中的B--->A 還是 E--->F?
答案:E--->F。
通過(guò):-XX:+CMSScavengeBeforeRemark ,觸發(fā)MonitorGC降低的就是YoungGen區(qū)的對(duì)象,從而達(dá)到標(biāo)記源頭減少,降低remark時(shí)間。
【思考??】CMS 跨代引用是圖一中的B--->A 有何危害,虛擬機(jī)是如何避開(kāi)的?
答案:CardTable。
邏輯:采用創(chuàng)建CardTable空間區(qū)域,來(lái)避免掃整個(gè)老年代。當(dāng)B無(wú)引用的時(shí)候,cardTable會(huì)被標(biāo)記,一次MonitorGC就會(huì)回收??掉。B是如何做到?jīng)]引用的,同E--->F是一樣的邏輯。

【思考??】CMS里有兩個(gè)需要STW的階段:initial mark,remark、這兩個(gè)標(biāo)記有什么不一樣么?使用的GC-Root一樣么?
答案:不一樣。
初始標(biāo)記:使用的是常規(guī)的GC-Root(虛擬機(jī)棧棧幀中的局部變量表、本地方法棧native方法、方法區(qū)中類的靜態(tài)屬性和方法、方法區(qū)中常量等引用的對(duì)象)
重新標(biāo)記:

  • 常規(guī)的GC-Root。(只有MonitorGC才會(huì)使用跟可達(dá)分析)
  • 新生代所有對(duì)象。
  • 遍歷老年代的DirtyCard(ModUnionTable)。

【思考??】同時(shí)帶來(lái)了另外一個(gè)問(wèn)題,MonitorGC可沒(méi)有遍歷整個(gè)老年代,而是采用CardTable通過(guò)時(shí)間換空間的做法,CMS-OldGC為什么采用遍歷新生代所有的對(duì)象呢?
??????? 這就是YoungGen 與 OldGen存在明顯的差異:

  • 老年代對(duì)象都是經(jīng)歷過(guò)多次GC回收??存活下來(lái)了,對(duì)象存在的變數(shù)極低。所以采用空間換時(shí)間效果較好。
  • 新生代對(duì)象屬于變化特別快的區(qū)域,如果采用空間換時(shí)間,既浪費(fèi)了空間,也沒(méi)有提升性能。

【思考??】既然initial mark階段+concurrent mark階段已經(jīng)掃果了young gen 為何還要再次Remark?

【思考??】CardTable與mod-union table有什么關(guān)系,都是干什么的?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。