是不是才聽說(shuō)了JDK11的ZGC,并且還沒搞懂?不好意思,OpenJDK12馬不停蹄的帶來(lái)了Shenandoah GC。
韶山網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)成立于2013年到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
JDK12新增的一個(gè)名為Shenandoah的GC算法,它的evacuation階段工作能通過(guò)與正在運(yùn)行中Java工作線程同時(shí)進(jìn)行(即并發(fā),concurrent),從而減少GC的停頓時(shí)間。
Shenandoah的停頓時(shí)間和堆的大小沒有任何關(guān)系,這就意味著無(wú)論你的堆是200MB,2GB還是200GB,停頓時(shí)間是一樣的。
如上圖所示,Shenandoah GC每個(gè)GC周期由2個(gè)STW(Stop The World)階段和2個(gè)并發(fā)階段組成。在初始化標(biāo)記階段,掃描root集合的時(shí)候會(huì)STW。然后并發(fā)標(biāo)記階段,Shenandoah GC和Java工作線程一起運(yùn)行,最后,在最終標(biāo)記階段,又會(huì)STW,然后執(zhí)行一個(gè)并發(fā)evacuation階段。
Root集合包括:thread local variables, references embedded in generated code, interned Strings, references from classloaders (e.g. static final references), JNI references, JVMTI references.
Shenandoah是一個(gè)基于Region設(shè)計(jì)的垃圾收集器,這點(diǎn)和G1類似,它把整個(gè)堆當(dāng)作Region集合來(lái)維護(hù)。但是,Shenandoah不需要remember set或者card table來(lái)記錄跨region引用。?
其中一個(gè)原因是(無(wú)條件)card mark可能引起false sharing,而Brooks pointer分散在每個(gè)對(duì)象頭上
,比較不容易引起false sharing:
一個(gè)常規(guī)的Shenandoah GC周期大概是這樣的(跟G1也有點(diǎn)相似):
GC日志如下:
GC(3)?Pause?Init?Mark?0.771msGC(3)?Concurrent?marking?76480M->77212M(102400M)?633.213msGC(3)?Pause?Final?Mark?1.821msGC(3)?Concurrent?cleanup?77224M->66592M(102400M)?3.112msGC(3)?Concurrent?evacuation?66592M->75640M(102400M)?405.312msGC(3)?Pause?Init?Update?Refs?0.084msGC(3)?Concurrent?update?references??75700M->76424M(102400M)?354.341msGC(3)?Pause?Final?Update?Refs?0.409msGC(3)?Concurrent?cleanup?76244M->56620M(102400M)?12.242ms
每個(gè)階段要做的事情如下:
Init Mark?并發(fā)標(biāo)記的初始化階段,它為并發(fā)標(biāo)記準(zhǔn)備堆和應(yīng)用線程,然后掃描root集合。這是整個(gè)GC生命周期第一次停頓,這個(gè)階段主要工作是root集合掃描,所以停頓時(shí)間主要取決于root集合大小。
Concurrent Marking?貫穿整個(gè)堆,以root集合為起點(diǎn),跟蹤可達(dá)的所有對(duì)象。 這個(gè)階段和應(yīng)用程序一起運(yùn)行,即并發(fā)(concurrent)。這個(gè)階段的持續(xù)時(shí)間主要取決于存活對(duì)象的數(shù)量,以及堆中對(duì)象圖的結(jié)構(gòu)。由于這個(gè)階段,應(yīng)用依然可以分配新的數(shù)據(jù),所以在并發(fā)標(biāo)記階段,堆占用率會(huì)上升。
Final Mark?清空所有待處理的標(biāo)記/更新隊(duì)列,重新掃描root集合,結(jié)束并發(fā)標(biāo)記。. 這個(gè)階段還會(huì)搞明白需要被清理(evacuated)的region(即垃圾收集集合),并且通常為下一階段做準(zhǔn)備。最終標(biāo)記是整個(gè)GC周期的第二個(gè)停頓階段,這個(gè)階段的部分工作能在并發(fā)預(yù)清理階段完成,這個(gè)階段最耗時(shí)的還是清空隊(duì)列和掃描root集合。
Concurrent Cleanup?回收即時(shí)垃圾區(qū)域 -- 這些區(qū)域是指并發(fā)標(biāo)記后,探測(cè)不到任何存活的對(duì)象。
Concurrent Evacuation?從垃圾收集集合中拷貝存活的對(duì)到其他的region中,這是有別于OpenJDK其他GC主要的不同點(diǎn)。這個(gè)階段能再次和應(yīng)用一起運(yùn)行,所以應(yīng)用依然可以繼續(xù)分配內(nèi)存,這個(gè)階段持續(xù)時(shí)間主要取決于選中的垃圾收集集合大?。ū热缯麄€(gè)堆劃分128個(gè)region,如果有16個(gè)region被選中,其耗時(shí)肯定超過(guò)8個(gè)region被選中)。
Init Update Refs?初始化更新引用階段,它除了確保所有GC線程和應(yīng)用線程已經(jīng)完成并發(fā)Evacuation階段,以及為下一階段GC做準(zhǔn)備以外,其他什么都沒有做。這是整個(gè)GC周期中,第三次停頓,也是時(shí)間最短的一次。
Concurrent Update References?再次遍歷整個(gè)堆,更新那些在并發(fā)evacuation階段被移動(dòng)的對(duì)象的引用。這也是有別于OpenJDK其他GC主要的不同,這個(gè)階段持續(xù)時(shí)間主要取決于堆中對(duì)象的數(shù)量,和對(duì)象圖結(jié)構(gòu)無(wú)關(guān),因?yàn)檫@個(gè)過(guò)程是線性掃描堆。這個(gè)階段是和應(yīng)用一起并發(fā)運(yùn)行的。
Final Update Refs?通過(guò)再次更新現(xiàn)有的root集合完成更新引用階段,它也會(huì)回收收集集合中的region,因?yàn)楝F(xiàn)在的堆已經(jīng)沒有對(duì)這些region中的對(duì)象的引用。
這是整個(gè)GC周期最后一個(gè)階段,它的持續(xù)時(shí)間主要取決于root集合的大小。
Concurrent Cleanup?回收那些現(xiàn)在沒有任何引用的Region集合。
Shenandoah不是一個(gè)要一統(tǒng)天下的GC,有一些其他的吞吐量?jī)?yōu)先,或者內(nèi)存占用優(yōu)先的GC算法,它們并不是把響應(yīng)性放在第一位(即不是主要考慮縮短停頓時(shí)間)。
Shenandoah是一個(gè)對(duì)那些更看重響應(yīng)性和可預(yù)測(cè)短暫停頓的應(yīng)用來(lái)說(shuō),更合適的GC算法。它的目標(biāo)不是要解決所有JVM的停頓問(wèn)題,由于GC之外的其他原因(例如到達(dá)安全點(diǎn)時(shí)間(TTSP--Time To Safe Point)問(wèn)題)而暫停時(shí)間超出了此JEP的范圍。
現(xiàn)代服務(wù)器比以前擁有更多的內(nèi)存和處理器,SLA應(yīng)用需要保證RP在10~500ms。為了達(dá)到
最苛刻的目標(biāo)(保證RP在10ms以內(nèi)),我們需要GC的算法足夠高效,允許程序在可用內(nèi)存中運(yùn)行,并且經(jīng)過(guò)優(yōu)化后,永遠(yuǎn)不會(huì)讓正在運(yùn)行的程序的停頓時(shí)間超過(guò)5毫秒(a handful of milliseconds,一只手就5根手指頭,所以是5ms)。
Shenandoah就是這樣一個(gè)OpenJDK為更近這個(gè)目標(biāo)而設(shè)計(jì)的開源、低停頓時(shí)間的垃圾回收器。
1. Zing/Azul是一個(gè)沒有停頓的垃圾收集器,但是不會(huì)貢獻(xiàn)給OpenJDK。
2. 基于colored pointers
設(shè)計(jì)的ZGC也是一個(gè)擁有很低停頓時(shí)間的垃圾收集器,Shenandoah期望能與之一戰(zhàn)。
3. G1很多工作都是并行或者并發(fā)的,但是evacuation階段不能并發(fā)執(zhí)行。
4. CMS能并發(fā)標(biāo)記,但是它執(zhí)行年輕代拷貝時(shí),需要STW,并且不會(huì)壓縮老年代,這就會(huì)導(dǎo)致花費(fèi)更多時(shí)間來(lái)管理老年代中的可用空間以及碎片問(wèn)題。
這還是一個(gè)體驗(yàn)功能,需要增加-XX:+UnlockExperimentalVMOptions參數(shù)才能開啟Shenandoah GC:
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
RedHat已經(jīng)做了大量的測(cè)試,OpenJDK也為Shenandoah開發(fā)了很多測(cè)試用例。而且從Fedora 24開始Shenandoah在Fedora中隨著JDK一起發(fā)布,并在Rhel7.4中作為技術(shù)預(yù)覽. 通過(guò)-XX:+UseShenandoahGC
運(yùn)行標(biāo)準(zhǔn)的OpenJDK完全足夠。
關(guān)于CMS,G1,ParallelOld,Shenandoah的延遲測(cè)試對(duì)比,如下圖所示: