JVM: G1和CMS的区别
CMS:以獲取最短回收停頓時間為目標(biāo)的收集器,基于并發(fā)“標(biāo)記清理”實現(xiàn)
有人會好奇為什么標(biāo)記清理算法會產(chǎn)生內(nèi)存碎片!但是CMS仍采用這種算法呢?
答案是:因為CMS作為第一款實現(xiàn)用戶線程和收集線程并發(fā)執(zhí)行的收集器!當(dāng)時的設(shè)計理念是減少停頓時間,最好是能并發(fā)執(zhí)行!但是問題來了,如要用戶線程也在執(zhí)行,那么就不能輕易的改變堆中對象的內(nèi)存地址!不然會導(dǎo)致用戶線程無法定位引用對象,從而無法正常運行!而標(biāo)記整理算法和復(fù)制算法都會移動存活的對象,這就與上面的策略不符!因此CMS采用的是標(biāo)記清理算法!
? ? ? ? ? ? ? 初始標(biāo)記-->并發(fā)標(biāo)記---->重新標(biāo)記---->并發(fā)清理
過程:
1、初始標(biāo)記:獨占PUC,stop-the-world, 僅標(biāo)記GCroots能直接關(guān)聯(lián)的對象
2、并發(fā)標(biāo)記:可以和用戶線程并發(fā)執(zhí)行,通過GCRoots Tracing 標(biāo)記所有可達(dá)對象。
3、重新標(biāo)記:獨占CPU,stop-the-world, 對并發(fā)標(biāo)記階段用戶線程運行產(chǎn)生的垃圾對象進(jìn)行標(biāo)記修正,以及更新自我拯救那部分逃逸對象
4、并發(fā)清理:可以和用戶線程并發(fā)執(zhí)行,清理垃圾
優(yōu)點:
并發(fā),低停頓
缺點:
1、對CPU非常敏感:在并發(fā)階段雖然不會導(dǎo)致用戶線程停頓,但是會因為占用了一部分線程使應(yīng)用程序變慢
2、無法處理浮動垃圾:在最后一步并發(fā)清理過程中,用戶線程執(zhí)行也會產(chǎn)生垃圾,但是這部分垃圾是在標(biāo)記之后,所以只有等到下一次gc的時候清理掉,這部分垃圾叫浮動垃圾。由于并發(fā)清理的時候,用戶線程也在運行,就需要保證用戶線程在運行的時候需要留有部分內(nèi)存以供使用。但是當(dāng)這部分內(nèi)存不足以給用戶線程正常使用時,就會出現(xiàn)一次 “Concurrent Mode Failure”,一旦出現(xiàn)了“Concurrent Mode Failure”,便會開啟后備方案,臨時使用SerialOld收集器進(jìn)行收集工作。
3、CMS使用“標(biāo)記-清理”法會產(chǎn)生大量的空間碎片,當(dāng)碎片過多,將會給大對象空間的分配帶來很大的麻煩,往往會出現(xiàn)老年代還有很大的空間但無法找到足夠大的連續(xù)空間來分配當(dāng)前對象,不得不提前觸發(fā)一次FullGC,
為了解決這個問題CMS提供了一個開關(guān)參數(shù),用于在CMS頂不住,要進(jìn)行FullGC時開啟內(nèi)存碎片的合并整理過程,但是內(nèi)存整理的過程是無法并發(fā)的,空間碎片沒有了但是停頓時間變長了
CMS 出現(xiàn)FullGC的原因:
1、年輕代晉升到老年代沒有足夠的連續(xù)空間,很有可能是內(nèi)存碎片導(dǎo)致的,因此會觸發(fā)FULL GC
2、在并發(fā)過程中JVM覺得在并發(fā)過程結(jié)束之前堆就會滿,需要提前觸發(fā)FullGC
CMS失敗后使用備案SerialOld收集器
G1:是一款面向服務(wù)端應(yīng)用的垃圾收集器
? ? ? ? ?初始標(biāo)記-->并發(fā)標(biāo)記---->最終標(biāo)記---->篩選回收
? ? ? ? ? ? G1只有并發(fā)標(biāo)記階段能做到用戶線程和回收線程并發(fā)執(zhí)行!!!!
G1可以不需要其它收集器配合就能獨立管理整個GC堆
目標(biāo)是替換掉CMS收集器
特點:
1、并行與并發(fā):G1能充分利用CPU、多核環(huán)境下的硬件優(yōu)勢,使用多個CPU(CPU或者CPU核心)來縮短stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執(zhí)行的GC動作,G1收集器仍然可以通過并發(fā)的方式讓java程序繼續(xù)執(zhí)行。
2、分代收集:分代概念在G1中依然得以保留。雖然G1可以不需要其它收集器配合就能獨立管理整個GC堆,但它能夠采用不同的方式去處理新創(chuàng)建的對象和已經(jīng)存活了一段時間、熬過多次GC的舊對象以獲取更好的收集效果。也就是說G1可以自己管理新生代和老年代了。
3、空間整合,沒有內(nèi)存碎片產(chǎn)生:由于G1使用了獨立區(qū)域(Region)概念,G1從整體來看是基于“標(biāo)記-整理”算法實現(xiàn)收集,從局部(兩個Region)上來看是基于“復(fù)制”算法實現(xiàn)的,但無論如何,這兩種算法都意味著G1運作期間不會產(chǎn)生內(nèi)存空間碎片。
在最后篩選回收階段,對每個region里的回收對象價值(回收該區(qū)域的時間消耗和能得到的內(nèi)存比值)最后進(jìn)行排序,用戶可以自定義停頓時間,那么G1就可以對部分的region進(jìn)行回收!這使得停頓時間是用戶自己可以控制的!!!!
但是每個region之間是有互相引用的依賴關(guān)系的!這導(dǎo)致在MinorGC的時候會同時對老年代進(jìn)行掃描(甚至是整個堆掃描),那就會導(dǎo)致MinorGC的效率低下,時間變長!
如何解決???
維護一個Remebered Set集合來存放各個Region之間的引用關(guān)系!當(dāng)進(jìn)行GC Roots Tracing 的時候就可以只掃描set里的關(guān)聯(lián)region!而不用全堆掃描啦!!!
4、可預(yù)測的停頓:這是G1相對于CMS的另一大優(yōu)勢,降低停頓時間是G1和CMS共同的關(guān)注點,但G1除了追求低停頓外,還能建立可預(yù)測的停頓時間模型,能讓使用這明確指定一個長度為M毫秒的時間片段內(nèi),消耗在垃圾收集上的時間不得超過N毫秒。
可預(yù)測的停頓什么意思呢?
G1可以有計劃的避免在整個JAV堆中進(jìn)行垃圾收集,可以對每個region里的回收對象價值(回收該區(qū)域的時間消耗和能得到的內(nèi)存比值)進(jìn)行分析,在最后篩選回收階段,對每個region里的回收對象價值(回收該區(qū)域的時間消耗和能得到的內(nèi)存比值)最后進(jìn)行排序,用戶可以自定義停頓時間,那么G1就可以對部分的region進(jìn)行回收!這使得停頓時間是用戶自己可以控制的!!!!
與其它收集器相比,G1變化較大的是它將整個Java堆劃分為多個大小相等的獨立區(qū)域(Region),雖然還保留了新生代和來年代的概念,但新生代和老年代不再是物理隔離的了它們都是一部分Region(不需要連續(xù))的集合。同時,為了避免全堆掃描,G1使用了Remembered Set來管理相關(guān)的對象引用信息。當(dāng)進(jìn)行內(nèi)存回收時,在GC根節(jié)點的枚舉范圍中加入Remembered Set即可保證不對全堆掃描也不會有遺漏了。
最后篩選回收階段首先對各個Region的回收價值和成本進(jìn)行排序,根據(jù)用戶所期望的GC停頓時間來制定回收計劃(可預(yù)測的停頓),這一過程同樣是需要停頓線程的,但Sun公司透露這個階段其實也可以做到并發(fā),但考慮到停頓線程將大幅度提高收集效率,所以選擇停頓。下圖為G1收集器運行示意圖:
?
總結(jié)
以上是生活随笔為你收集整理的JVM: G1和CMS的区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个例子理解什么是RPC?
- 下一篇: JVM:常用调优命令