到底是誰在回收 JVM 的垃圾~
來源 | 星塵的一個(gè)朋友(ID:rushjava)
開往虛擬機(jī)的車已經(jīng)出發(fā),關(guān)注上車

?虛擬機(jī)的垃圾回收器,沒有哪一個(gè)是絕對(duì)好的,只有比較好的。
?
今天的這篇文章,我要與你分享虛擬機(jī)的那些垃圾回收器們。內(nèi)容不多,可以耐心看完。
垃圾收集器
那些回收 JVM 垃圾的家伙
Serial
關(guān)鍵字:新生代、Stop The World 、標(biāo)記 - 復(fù)制算法、單線程
ParNew
關(guān)鍵字:新生代、Stop The World 、標(biāo)記 - 復(fù)制算法、多線程、「絕配」
Parallel Scavenge
關(guān)鍵字:新生代、Stop The World、標(biāo)記 - 復(fù)制算法、多線程、可控吞吐量【用戶線程執(zhí)行時(shí)間 /(用戶線程執(zhí)行時(shí)間+GC線程執(zhí)行時(shí)間)】、「自適應(yīng)策略」
上面這三種 「Serial」 、 「ParNew」 、 「Parallel Scavenge」 都是新生代的垃圾收集器,下面我們來看看老年代的垃圾收集器。
Serial Old
關(guān)鍵字:老年代、Stop The World、標(biāo)記 - 整理算法、單線程、「替補(bǔ)」
Parallel Old
關(guān)鍵字:老年代、Stop The World、標(biāo)記 - 整理算法、多線程、CP(組合)
CMS
關(guān)鍵字:老年代、Stop The World、標(biāo)記 - 清除算法、多線程、短停頓
具體步驟如下:
初始標(biāo)記:需要 Stop The World 并發(fā)標(biāo)記:與用戶線程并發(fā) 重新標(biāo)記:需要 Stop The World 并發(fā)清除:與用戶線程并發(fā)
標(biāo)記 GC Roots 直接關(guān)聯(lián)的對(duì)象(直達(dá)),速度較快,停頓時(shí)間短。
進(jìn)行標(biāo)記追蹤,完成全部對(duì)象的標(biāo)記任務(wù)??赡艹霈F(xiàn)漏標(biāo)或錯(cuò)標(biāo)情況。
修正并發(fā)標(biāo)記階段的對(duì)象標(biāo)記,因?yàn)榇蟛糠謱?duì)象不需要修正,所以執(zhí)行時(shí)間相比并發(fā)標(biāo)記時(shí)間短,但是停頓時(shí)間要比初始標(biāo)記停頓時(shí)間長(zhǎng)。
耗時(shí)較長(zhǎng),可以與用戶線程共同工作。
CMS 雖然有了一個(gè)較短的停頓時(shí)間,但是也有一些其他隨之而來的問題。
優(yōu)點(diǎn)
并發(fā)執(zhí)行速度快、停頓時(shí)間短。這一點(diǎn)沒得說,因?yàn)樗渲杏袃蓚€(gè)階段是和用戶線程并發(fā)。
缺點(diǎn)
「占用線程資源」,因?yàn)?CMS 工作有兩個(gè)階段是和用戶線程并發(fā),所以這里便會(huì)搶占用戶線程資源。 「浮動(dòng)垃圾」,清理一次之后還會(huì)有清理不掉的對(duì)象,需要在下次清理的時(shí)候才能夠清理到。 「空間利用率低」,因?yàn)椴l(fā)清理的原因,所以不能等到內(nèi)存完全用完之后再做清理,所以需要當(dāng)內(nèi)存使用達(dá)到一定閾值(默認(rèn)值68%,JDK6的時(shí)候提高到了 92%)時(shí)就開始進(jìn)行垃圾回收動(dòng)作,具體數(shù)值可以通過參數(shù)控制。 「空間碎片」,因?yàn)槭褂脴?biāo)記-清除算法的原因,會(huì)導(dǎo)致碎片空間的產(chǎn)生。CMS 的做法是在其不能夠滿足對(duì)象分配任務(wù)的時(shí)候,F(xiàn)ULL GC 的時(shí)候,會(huì)進(jìn)行一次空間整理的動(dòng)作。 對(duì)這個(gè)整理的動(dòng)作也是有參數(shù)可以進(jìn)行控制,參數(shù)設(shè)置情況為,滿足幾次FullGC之后,進(jìn)行一次空間整理,默認(rèn)值為 0 ,即每次 full gc 都會(huì)進(jìn)行一次空間整理。
這一點(diǎn)雖然是缺點(diǎn),不過 CMS 已經(jīng)盡力去彌補(bǔ)了,包括這里的 FULL GC 之后的內(nèi)存空間整理,還有對(duì)象分配時(shí) CMS 會(huì)在 Free List 申請(qǐng)一塊較大的內(nèi)存空間,然后通過指針碰撞的方式來進(jìn)行對(duì)象分配,盡可能減小空間碎片的產(chǎn)生。
空間碎片問題也是 CMS 不能直接使用指針碰撞的方式來為對(duì)象分配內(nèi)存的原因。
這里的原因是因?yàn)椴l(fā)清除階段是和用戶線程并發(fā),一邊清除一邊使用,可能會(huì)出現(xiàn)一些無法清理掉的新生垃圾,比如清理過程中,程序斷開了某個(gè)引用,被斷開的引用 GC Roots 不可達(dá),所以這個(gè)被斷開的引用指向的對(duì)象變成了浮動(dòng)垃圾。
這里 JVM 給了風(fēng)險(xiǎn)預(yù)案:凍結(jié)用戶線程,啟動(dòng) serial old 來進(jìn)行一次老年代垃圾回收。這也是上面我們說 Serial Old 的時(shí)候,提到他的關(guān)鍵字里有 “替補(bǔ)” 的原因。
Garbage First (G1)
關(guān)鍵字:里程碑、JDK 9、區(qū)域管理、按需回收、「延遲可控的最高吞吐量」
?多了解一點(diǎn),關(guān)于 G1 的工作模式,Mixed Mode 的擴(kuò)展:G1 有純 GC 模式和分代回收模式,分代模式會(huì)分為 Minor GC 和 Mixed GC 兩種,這里的模式選擇會(huì)影響最后的篩選回收階段的回收集合的內(nèi)容。這塊內(nèi)容可參考后面留的 R大 的鏈接
?
下面我們一起了解一下 G1 工作的具體步驟:
初始標(biāo)記:Stop The World 并發(fā)標(biāo)記:與用戶線程并發(fā) 最終標(biāo)記:Stop The World 篩選回收:Stop The World
標(biāo)記 GC Roots 直接關(guān)聯(lián)的對(duì)象,同時(shí)修改 TAMS 指針
標(biāo)記全部要回收的對(duì)象,與用戶線程并發(fā),標(biāo)記完成之后,重新處理 SATB 記錄下在并發(fā)時(shí)有引用變動(dòng)的對(duì)象
處理并發(fā)標(biāo)記階段 SATB 遺留的引用,同時(shí)這個(gè)階段也進(jìn)行弱引用處理。
這個(gè)階段會(huì)更新 Region 的統(tǒng)計(jì)數(shù)據(jù),對(duì)每個(gè) Region 根據(jù)其回收價(jià)值和成本進(jìn)行排序,然后根據(jù)用戶所期望的停頓時(shí)間(參數(shù)設(shè)置)來制定一個(gè)回收計(jì)劃。
再根據(jù)這個(gè)計(jì)劃,選擇任意 Region 來組成一個(gè)回收集(collection set)。將回收集中的 Region(被選中的區(qū)域) 中的存活對(duì)象復(fù)制到空的 Region 中,然后將舊的(選中的) Region 清理掉。
以上過程由多線程并行完成,同時(shí)因?yàn)橐苿?dòng)對(duì)象需要暫停用戶線程(Stop The World)
?TAMS:Top at Mark Start Region 中的兩個(gè)指針名稱,他們的作用是將 Region 的一部分空間劃分出來給并發(fā)回收過程中程序運(yùn)行產(chǎn)生的新對(duì)象使用
SATB:原始快照,還記得之前我們對(duì)漏標(biāo)的解決方案嗎?一種是增量更新(CMS 采用的這種方案),另外一個(gè)就是原始快照,這里可以翻翻之前的內(nèi)容。
?
G1除了并發(fā)標(biāo)記階段都需要暫停用戶線程
G1的理想目標(biāo):在延遲可控的情況下達(dá)到最大的吞吐量
用戶可以通過參數(shù)設(shè)置所期望的停頓時(shí)間,這個(gè)時(shí)間一般建議設(shè)置為 100 ~ 300 ms。
垃圾收集器小結(jié)
上面一共說了 7 款垃圾收集器,不過他們的具體使用我覺得有必要了解一下。
按照分工劃分
「新生代」Serial、ParNew、Parallel Scavenge
「老年代」Serial Old、Parallel Old、CMS
「內(nèi)存區(qū)域」:G1
按照搭配組合劃分


Serial + Serial Old 的單線程組合,適用于資源受限的場(chǎng)景。 ParNew + CMS 這組曾經(jīng)的王者組合,新生代的多線程并行高性能加上老年代的短暫停頓組合,可以應(yīng)對(duì)大部分場(chǎng)景。 Parallel Scavenge + Serial Old 這是 Paralle Old 沒出現(xiàn)的時(shí)候的應(yīng)對(duì)組合(不明白為何 JDK 9 的時(shí)候沒取消這對(duì)奇葩組合) Parallel Scavenge + Parallel Old 這對(duì)高吞吐量 CP
G1的出現(xiàn)
現(xiàn)狀
綜上所述,到 JDK 9 之后還剩下建議使用的組合如下
G1 Serial + Serial Old Parallel Scavenge + Serial Old (還是它,我一定要關(guān)注它倆啥時(shí)候下崗)
所以通過上面的分析,也能看出來 HotSpot 的用意,在 JDK 9 以后就是要將 G1 作為一個(gè)全能型的垃圾收集器來發(fā)展。
寫在最后
https://www.iteye.com/blog/rednaxelafx-362738
往 期 推 薦 1、阿里云盤正式公測(cè)!免費(fèi)領(lǐng)1年云盤擴(kuò)容碼,速來,先到先得! 2、牛逼!IntelliJ IDEA居然支持視頻聊天了~速來嘗鮮!快來沖一波 3、如何設(shè)置最近爆火的微信開關(guān)頭像?想學(xué)啊,我教你啊~ 4、知名國產(chǎn)網(wǎng)盤翻車?清空免費(fèi)用戶文件后,又開始清理付費(fèi)用戶資源 5、Chrome新功能曝光:你訪問的敏感網(wǎng)站可以自動(dòng)隱藏起來 6、萬萬沒想到,“紅孩兒”竟然做了程序員,還是CTO! 7、徒手?jǐn)]一個(gè)Spring Boot中的starter,解密自動(dòng)化配置,超級(jí)棒!

點(diǎn)分享

點(diǎn)收藏

點(diǎn)點(diǎn)贊

點(diǎn)在看

