大連小型網(wǎng)站建設(shè)關(guān)鍵的近義詞
🔭 嗨,您好 👋 我是 vnjohn,在互聯(lián)網(wǎng)企業(yè)擔(dān)任 Java 開發(fā),CSDN 優(yōu)質(zhì)創(chuàng)作者
📖 推薦專欄:Spring、MySQL、Nacos、Java,后續(xù)其他專欄會(huì)持續(xù)優(yōu)化更新迭代
🌲文章所在專欄:JVM
🤔 我當(dāng)前正在學(xué)習(xí)微服務(wù)領(lǐng)域、云原生領(lǐng)域、消息中間件等架構(gòu)、原理知識(shí)
💬 向我詢問(wèn)任何您想要的東西,ID:vnjohn
🔥覺(jué)得博主文章寫的還 OK,能夠幫助到您的,感謝三連支持博客🙏
😄 代詞: vnjohn
? 有趣的事實(shí):音樂(lè)、跑步、電影、游戲
目錄
- 前言
- 可達(dá)性分析算法
- 標(biāo)記過(guò)程
- 三色標(biāo)記算法
- 多標(biāo)
- 漏標(biāo)
- Compare
- 總結(jié)
前言
回顧 優(yōu)化內(nèi)存利用:深入了解垃圾回收算法與回收器 一文,介紹了垃圾回收算法、垃圾收集器(垃圾收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的實(shí)踐者)
主要談到了 CMS、G1 這兩種垃圾收集器,它們都有一個(gè)共同的特征,可支持并發(fā)標(biāo)記,從此文來(lái)介紹它們兩者在并發(fā)標(biāo)記階段共同所使用的算法
可達(dá)性分析算法
當(dāng)前主流編程語(yǔ)言的垃圾收集器基本上都是依靠可達(dá)性分析算法來(lái)判定對(duì)象是否存活的,可達(dá)性分析算法理論上要求全過(guò)程都基于一個(gè)能保障一致性的快照才能進(jìn)行分析,這意味著必須全程凍結(jié)用戶線程的運(yùn)行
在 JVM 整個(gè)內(nèi)存結(jié)構(gòu)布局中,堆的大小占比是特別大的,堆越大,存儲(chǔ)的對(duì)象就越多,對(duì)象圖結(jié)構(gòu)就越復(fù)雜,從而就要標(biāo)記更多的對(duì)象而產(chǎn)生的 STW 時(shí)間就會(huì)越長(zhǎng)
“標(biāo)記階段”是所有垃圾收集算法的共同特征,若這個(gè)階段隨著堆變大而增加停頓時(shí)間,其所有的垃圾收集器都會(huì)在這個(gè)階段都會(huì)波及到一定影響,若能夠削減這部分帶來(lái)的停頓時(shí)間,收益也將會(huì)是系統(tǒng)性的提高
標(biāo)記過(guò)程
簡(jiǎn)要回顧一下 CMS、G1 兩者垃圾收集器在進(jìn)行垃圾回收時(shí)所要發(fā)生的四個(gè)階段:
- CMS:初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除
- G1:初始標(biāo)記、并發(fā)標(biāo)記、最終標(biāo)記、篩選回收
CMS、G1 兩者都有并發(fā)標(biāo)記這個(gè)階段,導(dǎo)致了它們兩者在用戶線程、GC 回收線程同時(shí)運(yùn)行時(shí)負(fù)載會(huì)有所不同,同時(shí)也會(huì)出現(xiàn)一些引用標(biāo)記的問(wèn)題「多標(biāo)、漏標(biāo)」
三色標(biāo)記算法
若要解決或降低用戶線程的停頓,首先就會(huì)搞清楚為什么必須在一個(gè)能保障一致性的快照上同時(shí)還能進(jìn)行對(duì)象圖的遍歷操作?
能保障一致性的快照上同時(shí)還能進(jìn)行對(duì)象圖的遍歷操作:指的就是并發(fā)標(biāo)記階段
在垃圾收集器中實(shí)踐階段,引入了 “三色標(biāo)記” 算法作為輔助工具,把遍歷對(duì)象圖過(guò)程中遇到的對(duì)象,按照 “是否訪問(wèn)過(guò)” 這個(gè)條件來(lái)標(biāo)記成三種不同顏色的引用對(duì)象
- 白色:表示對(duì)象尚未被垃圾收集器訪問(wèn)過(guò),在根可達(dá)分析剛開始的階段,所有對(duì)象都是白色的,若在標(biāo)記階段結(jié)束后,仍然是白色的對(duì)象,即代表不可達(dá),對(duì)象就會(huì)被回收 > 處于消亡狀態(tài)
- 黑色:表示對(duì)象以及被垃圾收集器訪問(wèn)過(guò),且這個(gè)對(duì)象關(guān)聯(lián)的所有引用都已經(jīng)被掃描過(guò);黑色的對(duì)象代表已經(jīng)被掃描過(guò),它是安全存活的,若其他對(duì)象引用指向了黑色對(duì)象,無(wú)須再重新掃描一遍
- 灰色:表示對(duì)象已經(jīng)被垃圾收集器訪問(wèn)過(guò),但這個(gè)對(duì)象上至少存在一個(gè)引用(屬性對(duì)象)還沒(méi)被掃描過(guò)
初始狀態(tài),只有 GC Roots 是黑色的
標(biāo)記過(guò)程:并發(fā)垃圾收集的標(biāo)記階段過(guò)程中,灰色對(duì)象的標(biāo)記狀態(tài)不斷向前推進(jìn),從黑色對(duì)象(已完成標(biāo)記)擴(kuò)展到白色對(duì)象(尚未被訪問(wèn))灰色對(duì)象是黑、白對(duì)象之間的分水嶺
從根對(duì)象逐步向下掃描,相當(dāng)于就是在對(duì)象圖上從黑向白推進(jìn)「灰色對(duì)象作為黑、白兩者之間的分水嶺」過(guò)程,從 Serial 系列、Parallel 系列垃圾收集器來(lái)看,當(dāng)出現(xiàn)垃圾收集時(shí),它們的用戶線程是處于凍結(jié)態(tài)的,只有垃圾收集線程是處于工作態(tài)的,就不會(huì)出現(xiàn)對(duì)象圖很亂的問(wèn)題
故而言之,Serial 系列、Parallel 系列收集器無(wú)須額外使用三色標(biāo)記算法去處理,它們采用追蹤式垃圾收集算法(標(biāo)記-復(fù)制、標(biāo)記-清除、標(biāo)記-整理)處理即可
標(biāo)記完成:標(biāo)記階段完成,此時(shí)黑色對(duì)象就是存活的對(duì)象,白色對(duì)象就是已消亡且可回收對(duì)象
可是,當(dāng)使用了 CMS 或 G1 垃圾收集器(并發(fā)垃圾收集器,支持垃圾收集線程、用戶線程并發(fā)執(zhí)行)時(shí),這時(shí)候情況就不一樣了,垃圾收集線程會(huì)在對(duì)象圖結(jié)構(gòu)上進(jìn)行顏色標(biāo)記,同時(shí)用戶線程也在修改引用關(guān)系(即修改對(duì)象圖的結(jié)構(gòu))這時(shí)就會(huì)出現(xiàn)兩種后果
多標(biāo)、漏標(biāo)
多標(biāo)
多標(biāo):將原來(lái)消亡的對(duì)象錯(cuò)誤標(biāo)記為存活,這不是好事,增加了額外的內(nèi)存空間浪費(fèi),但其實(shí)是可以容忍的,只不過(guò)產(chǎn)生了一些逃過(guò)本次垃圾收集產(chǎn)生的浮動(dòng)垃圾而已,下次再進(jìn)行清理即可
在顏色狀態(tài)推進(jìn)的過(guò)程中,正在掃描的黑對(duì)象引用切斷與灰色對(duì)象關(guān)系(此時(shí)應(yīng)已被標(biāo)記為消亡態(tài)),但此時(shí)又有另外一個(gè)黑色對(duì)象將其標(biāo)記了(此時(shí)又由最初的消亡態(tài)變換為存活態(tài))
漏標(biāo)
漏標(biāo):將原來(lái)存活的對(duì)象錯(cuò)誤標(biāo)記為已消亡,這種情況下就會(huì)比較嚴(yán)重,程序會(huì)因此發(fā)生程序錯(cuò)誤,例如:Class Xxx Not Found
當(dāng)且僅有兩個(gè)條件同時(shí)滿足時(shí),會(huì)造成漏標(biāo)問(wèn)題的產(chǎn)生,即原本應(yīng)當(dāng)是黑色對(duì)象的被誤標(biāo)記為白色對(duì)象
- 插入了一條或多條從黑色對(duì)象到白色對(duì)象的新引用
- 刪除了所有從灰色對(duì)象到白色對(duì)象的新引用
因此,要解決在并發(fā)標(biāo)記階段所造成的漏標(biāo)問(wèn)題,只需要破壞這兩個(gè)條件中的任意一個(gè)條件即可,分別產(chǎn)生了以下兩種解決方案:增量更新(Increment Update)以及原始快照(Snapshot At The Beginning -> SATB)
我們來(lái)看造成漏標(biāo)問(wèn)題的第一個(gè)條件是如何產(chǎn)生的,如下圖:
結(jié)合第一張、第二張圖來(lái)看,再看上圖,在顏色狀態(tài)推進(jìn)的過(guò)程中,正在掃描的灰色對(duì)象引用切斷與白色對(duì)象的聯(lián)系,同時(shí)原來(lái)白色引用的對(duì)象又已經(jīng)跟掃描過(guò)的黑對(duì)象建立了引用關(guān)系
增量更新的方案是破壞了第一個(gè)條件,當(dāng)黑色對(duì)象插入新的指向白色對(duì)象關(guān)系時(shí),就將這個(gè)新插入的引用記錄下來(lái),等待并發(fā)標(biāo)記階段結(jié)束以后的下一個(gè)階段,再將這些新插入的引用記錄,以黑色對(duì)象為根,重新掃描一次
CMS 采用的就是增量更新的方式來(lái)處理漏標(biāo)問(wèn)題,在它的重新標(biāo)記階段進(jìn)行處理,可以簡(jiǎn)單理解為,一旦黑色對(duì)象插入了新的指向白色對(duì)象的引用,它就會(huì)變?yōu)榛疑珜?duì)象
我們來(lái)看造成漏標(biāo)問(wèn)題的第二個(gè)條件是如何產(chǎn)生的,如下圖:
被切斷后的對(duì)象重新被黑色對(duì)象所引用的對(duì)象可能是原有引用鏈中的一部分,由于黑色對(duì)象只會(huì)掃描一次,這將導(dǎo)致掃描結(jié)束后出現(xiàn)兩個(gè)被黑色對(duì)象所引用的對(duì)象仍是白色,所以這兩個(gè)白色對(duì)象就會(huì)消失,這種情況就很嚴(yán)重了
原始快照的方案就是破壞了第二個(gè)條件,當(dāng)灰色對(duì)象要?jiǎng)h除指向白色對(duì)象的引用關(guān)系時(shí),就將這個(gè)要?jiǎng)h除的引用記錄下來(lái),在并發(fā)標(biāo)記階段后的下一個(gè)階段,再將這些刪除的引用關(guān)系以灰色對(duì)象為根,重新進(jìn)行一次掃描工作
G1 采用的就是原始快照 SATB 方式來(lái)處理漏標(biāo)問(wèn)題的,在它的最終標(biāo)記階段進(jìn)行處理,也可以簡(jiǎn)單理解為,無(wú)論引用關(guān)系刪除與否,都會(huì)按照剛開始掃描的那一刻對(duì)象圖快照記錄來(lái)進(jìn)行重新掃描
Compare
增量更新、原始快照方式,無(wú)論是對(duì)引用關(guān)系的插入還是刪除,它們的記錄操作都是通過(guò)寫屏障技術(shù)來(lái)完成的
寫屏障技術(shù)被用于記錄對(duì)象的標(biāo)記狀態(tài),寫屏障技術(shù)一旦有引用關(guān)系發(fā)生了變化,它都會(huì)進(jìn)行記錄,但現(xiàn)有的 CMS、G1 都采用了插入式寫屏障技術(shù)來(lái)進(jìn)行優(yōu)化,減少了一些性能上的開銷工作
考慮性能的高低以及兩者之間的權(quán)衡來(lái)決定以黑為根還是以灰為根來(lái)進(jìn)行一次重新掃描工作
增量更新:會(huì)重新以黑色對(duì)象為根進(jìn)行重新掃描(黑色—>白色),會(huì)浪費(fèi)多一些時(shí)間,但考慮到發(fā)生漏標(biāo)問(wèn)題的情況也不太常見,所以掃描這部分黑色對(duì)象自然也就不多
原始快照:可能會(huì)把原本要取消引用的對(duì)象(灰色—>白色)給錯(cuò)誤的標(biāo)記為存活狀態(tài)了,從而會(huì)產(chǎn)生一些浮動(dòng)垃圾,也就是前面所說(shuō)到的多標(biāo)問(wèn)題,能夠被忽略
總結(jié)
該篇博文主要介紹了 CMS、G1 在「并發(fā)標(biāo)記」階段共同使用到的一種算法:三色標(biāo)記算法,簡(jiǎn)要說(shuō)明了它的多標(biāo)問(wèn)題,重點(diǎn)介紹了在使用其算法時(shí)會(huì)發(fā)生的漏標(biāo)問(wèn)題,有兩種方式可以用來(lái)解決這種問(wèn)題:增量更新、原始快照,CMS 使用的是前者,G1 使用的后者,最后對(duì)這兩種不同解決方案方式作了一下對(duì)比,希望此博文你能夠喜歡!
參考文獻(xiàn):《深入理解 Java 虛擬機(jī)》周志明著
博文放在 JVM 專欄里,歡迎訂閱,會(huì)持續(xù)更新!
如果覺(jué)得博文不錯(cuò),關(guān)注我 vnjohn,后續(xù)會(huì)有更多實(shí)戰(zhàn)、源碼、架構(gòu)干貨分享!
推薦專欄:Spring、MySQL,訂閱一波不再迷路
大家的「關(guān)注?? + 點(diǎn)贊👍 + 收藏?」就是我創(chuàng)作的最大動(dòng)力!謝謝大家的支持,我們下文見!