GC Roots只占堆的很小部分,并且在各種優(yōu)化技巧(如OopMap)的加持下,帶來的停頓已經(jīng)是非常短暫且固定的了。
可從GC Roots再繼續(xù)往下遍歷對(duì)象圖,這一步驟的停頓就要與Java堆的容量直接成正比了。
為了降低線程停頓時(shí)間,就需要并發(fā)的進(jìn)行可達(dá)性分析。如下就是并發(fā)可達(dá)性分析的細(xì)節(jié)。
1、三色標(biāo)記
白色: 未被掃描過
黑色: 自己以及子引用均被掃描過
灰色: 掃描過,但至少還存在一個(gè)引用沒有被掃描過。
2、動(dòng)態(tài)掃描會(huì)有什么問題
GC Roots掃描過程中,用戶線程動(dòng)態(tài)的改變了對(duì)象的引用,造成如下兩種情況
1、標(biāo)記為存活的對(duì)象,不需要了,變成可清除得了。 ---這種情況可以容忍,產(chǎn)生了浮動(dòng)垃圾,下次就可以清除。
2、標(biāo)記為清除的對(duì)象,變成了需要存活。 ---這種情況程序一定發(fā)生錯(cuò)誤。
如下圖:

3、分析情況
當(dāng)以下兩個(gè)條件同時(shí)滿足的時(shí)候,才會(huì)發(fā)生黑色對(duì)象誤標(biāo)記為白色對(duì)象。
1、賦值器插入了一條或多條從黑色對(duì)象到白色對(duì)象的新引用。
2、賦值器刪除了全部從灰色對(duì)象到該白色對(duì)象的直接或間接引用。
4、如何解決
只要破壞了上述其中一條即可
1、增量更新
破壞第一個(gè)條件
當(dāng)黑色對(duì)象插入新的指向白色對(duì)象的引用關(guān)系時(shí),就將這個(gè)新插入的引用記錄下來,等并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的黑色對(duì)象為根,重新掃描一次。這可以簡(jiǎn)化理解為,黑色對(duì)象一旦新插入了指向白色對(duì)象的引用之后,它就變回灰色對(duì)象了。
例如CMS。
2、原始快照
破壞第二個(gè)條件
當(dāng)灰色對(duì)象要?jiǎng)h除指向白色對(duì)象的引用關(guān)系時(shí),就將這個(gè)要?jiǎng)h除的引用記錄下來,在并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的灰色對(duì)象為跟,重新掃描一次。這也可以簡(jiǎn)化理解為,無論引用關(guān)系刪除與否,都會(huì)按照剛剛開始掃描那一刻的對(duì)象圖快照來進(jìn)行搜索。
例如G1、Shenandoah。
以上無論是對(duì)引用關(guān)系記錄的插入還是刪除,虛擬機(jī)的記錄操作都是通過寫屏障實(shí)現(xiàn)的。
CMS使用的是增量更新。