JDK 13 的最新垃圾回收器ZGC,你了解多少?
??點(diǎn)擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號
重磅資訊、干貨,第一時(shí)間送達(dá) 今日推薦:Java實(shí)現(xiàn)QQ登錄和微博登錄個(gè)人原創(chuàng)+1博客:點(diǎn)擊前往,查看更多 作者:馬小邱 來源:https://segmentfault.com/a/1190000022107708JDK13已經(jīng)發(fā)布有一段時(shí)間了,年前沒有細(xì)看,單純的就是瀏覽了一下,沒成想,疫情在家,假期從寒假放到了暑假,閑來無聊,突然想起來他,就在網(wǎng)上查了一下相關(guān)的文檔和資料,在即將開工之前,進(jìn)行一個(gè)整理
首先肯定奉上的是我自己覺得比較有意思的幾大特性和大家一起分享
350: Dynamic CDS Archives
351: ZGC: Uncommit Unused Memory
353: Reimplement the Legacy Socket API
354: Switch Expressions (Preview)
355: Text Blocks (Preview)
在這里面,我覺得最有意思的是兩個(gè),ZGC和Switch,其中ZGC因?yàn)镚C這個(gè)名詞以及JVM調(diào)優(yōu)的緣故,關(guān)注比較多,今天就來跟大家分享一下我對于ZGC的理解。
什么是ZGC
ZGC(Z Garbage Collector)是一款由Oracle公司研發(fā)的,以低延遲為首要目標(biāo)的一款垃圾收集器。它是基于動態(tài)Region內(nèi)存布局,(暫時(shí))不設(shè)年齡分代,使用了讀屏障、染色指針和內(nèi)存多重映射等技術(shù)來實(shí)現(xiàn)可并發(fā)的標(biāo)記-整理算法的收集器。在JDK 11新加入,還在實(shí)驗(yàn)階段,主要特點(diǎn)是:回收TB級內(nèi)存(最大4T),停頓時(shí)間不超過10ms。
動態(tài)Region
ZGC的Region可以具有如圖所示的大、中、小三類容量:
· 小型Region(Small Region):容量固定為2MB,用于放置小于256KB的小對象。
· 中型Region(Medium Region):容量固定為32MB,用于放置大于等于256KB但小于4MB的對象。·
· 大型Region(Large Region):容量不固定,可以動態(tài)變化,但必須為2MB的整數(shù)倍,用于放置4MB或以上的大對象。每個(gè)大型Region中只會存放一個(gè)大對象,最小容量可低至4MB,所有大型Region可能小于中型Region。大型Region在ZGC的實(shí)現(xiàn)中是不會被重分配的,因?yàn)閺?fù)制一個(gè)大對象的代價(jià)非常高昂。
染色指針技術(shù)
HotSpot虛擬機(jī)的標(biāo)記實(shí)現(xiàn)方案有如下幾種:
1. 把標(biāo)記直接記錄在對象頭上(如Serial收集器);
2. 把標(biāo)記記錄在與對象相互獨(dú)立的數(shù)據(jù)結(jié)構(gòu)上(如G1、Shenandoah使用了一種相當(dāng)于堆內(nèi)存的1/64大小的,稱為BitMap的結(jié)構(gòu)來記錄標(biāo)記信息);
3. 直接把標(biāo)記信息記在引用對象的指針上(如ZGC)
染色指針是一種直接將少量額外的信息存儲在指針上的技術(shù)。目前在Linux下64位的操作系統(tǒng)中高18位是不能用來尋址的,但是剩余的46位卻可以支持64T的空間,到目前為止我們幾乎還用不到這么多內(nèi)存。于是ZGC將46位中的高4位取出,用來存儲4個(gè)標(biāo)志位,剩余的42位可以支持4T的內(nèi)存,如圖所示:
· Linux下64位指針的高18位不能用來尋址,所有不能使用;
· Finalizable:表示是否只能通過finalize()方法才能被訪問到,其他途徑不行;
· Remapped:表示是否進(jìn)入了重分配集(即被移動過);
· Marked1、Marked0:表示對象的三色標(biāo)記狀態(tài);
· 最后42用來存對象地址,最大支持4T;
三色標(biāo)記
在并發(fā)的可達(dá)性分析算法中我們使用三色標(biāo)記(Tri-color Marking)來標(biāo)記對象是否被收集器訪問過:
· 白色:表示對象尚未被垃圾收集器訪問過。顯然在可達(dá)性分析剛剛開始的階段,所有的對象都是白色的,若在分析結(jié)束的階段,仍然是白色的對象,即代表不可達(dá)。
· 黑色:表示對象已經(jīng)被垃圾收集器訪問過,且這個(gè)對象的所有引用都已經(jīng)掃描過。黑色的對象代表已經(jīng)掃描過,它是安全存活的,如果有其他對象引用指向了黑色對象,無須重新掃描一遍。黑色對象不可能直接(不經(jīng)過灰色對象)指向某個(gè)白色對象。
· 灰色:表示對象已經(jīng)被垃圾收集器訪問過,但這個(gè)對象上至少存在一個(gè)引用還沒有被掃描過。
可達(dá)性分析的掃描過程,其實(shí)就是一股以灰色為波峰的波紋從黑向白推進(jìn)的過程,但是在并發(fā)的推進(jìn)過程中會產(chǎn)生“對象消失”的問題,如圖:
對象消失理論,只有同時(shí)滿足才會發(fā)生對象消失:
· 賦值器插入了一條或多條從黑色對象到白色對象的新引用;
· 賦值器刪除了全部從灰色對象到該白色對象的直接或間接引用;
要解決對象消失問題只需要破壞其中一條就行了,目前常用有兩種方案:
· 增量更新(Incremental Update):增量更新要破壞的是第一個(gè)條件,當(dāng)黑色對象插入新的指向白色對象的引用關(guān)系時(shí),就將這個(gè)新插入的引用記錄下來,等并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的黑色對象為根,重新掃描一次。這可以簡化理解為,黑色對象一旦新插入了指向白色對象的引用之后,它就變回灰色對象了。
· 原始快照(Snapshot At TheBeginning,SATB):原始快照要破壞的是第二個(gè)條件,當(dāng)灰色對象要刪除指向白色對象的引用關(guān)系時(shí),就將這個(gè)要刪除的引用記錄下來,在并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的灰色對象為根,重新掃描一次。這也可以簡化理解為,無論引用關(guān)系刪除與否,都會按照剛剛開始掃描那一刻的對象圖快照來進(jìn)行搜索。
以上無論是對引用關(guān)系記錄的插入還是刪除,虛擬機(jī)的記錄操作都是通過寫屏障實(shí)現(xiàn)的。CMS是基于增量更新來做并發(fā)標(biāo)記的,G1、Shenandoah則是用原始快照來實(shí)現(xiàn)。
染色指針的三大優(yōu)勢
1. 一旦某個(gè)Region的存活對象被移走之后,這個(gè)Region立即就能夠被釋放和重用掉,而不必等待整個(gè)堆中所有指向該Region的引用都被修正后才能清理,這使得理論上只要還有一個(gè)空閑Region,ZGC就能完成收集。而Shenandoah需要等到更新階段結(jié)束才能釋放回收集中的Region,如果Region里面對象都存活的時(shí)候,需要1:1的空間才能完成收集。
2. 染色指針可以大幅減少在垃圾收集過程中內(nèi)存屏障的使用數(shù)量,ZGC只使用了讀屏障。
3. 染色指針具備強(qiáng)大的擴(kuò)展性,它可以作為一種可擴(kuò)展的存儲結(jié)構(gòu)用來記錄更多與對象標(biāo)記、重定位過程相關(guān)的數(shù)據(jù),以便日后進(jìn)一步提高性能。
內(nèi)存多重映射
ZGC使用了內(nèi)存多重映射(Multi-Mapping)將多個(gè)不同的虛擬內(nèi)存地址映射到同一個(gè)物理內(nèi)存地址上,這是一種多對一映射,意味著ZGC在虛擬內(nèi)存中看到的地址空間要比實(shí)際的堆內(nèi)存容量來得更大。把染色指針中的標(biāo)志位看作是地址的分段符,那只要將這些不同的地址段都映射到同一個(gè)物理內(nèi)存空間,經(jīng)過多重映射轉(zhuǎn)換后,就可以使用染色指針正常進(jìn)行尋址了,效果如圖:
ZGC的多重映射只是它采用染色指針技術(shù)的伴生產(chǎn)物
讀屏障
當(dāng)對象從堆中加載的時(shí)候,就會使用到讀屏障(Load Barrier)。這里使用讀屏障的主要作用就是檢查指針上的三色標(biāo)記位,根據(jù)標(biāo)記位判斷出對象是否被移動過,如果沒有可以直接訪問,如果移動過就需要進(jìn)行“自愈”(對象訪問會變慢,但也只會有一次變慢),當(dāng)“自愈”完成后,后續(xù)訪問就不會變慢了。
讀寫屏障可以理解成對象訪問的“AOP”操作
ZGC運(yùn)作過程
ZGC的運(yùn)作過程大致可劃分為以下四個(gè)大的階段:
· 并發(fā)標(biāo)記(Concurrent Mark):與G1、Shenandoah一樣,并發(fā)標(biāo)記是遍歷對象圖做可達(dá)性分析的階段,它的初始標(biāo)記和最終標(biāo)記也會出現(xiàn)短暫的停頓,整個(gè)標(biāo)記階段只會更新染色指針中的Marked 0、Marked 1標(biāo)志位。
· 并發(fā)預(yù)備重分配(Concurrent Prepare for Relocate):這個(gè)階段需要根據(jù)特定的查詢條件統(tǒng)計(jì)得出本次收集過程要清理哪些Region,將這些Region組成重分配集(Relocation Set)。ZGC每次回收都會掃描所有的Region,用范圍更大的掃描成本換取省去G1中記憶集的維護(hù)成本。
· 并發(fā)重分配(Concurrent Relocate):重分配是ZGC執(zhí)行過程中的核心階段,這個(gè)過程要把重分配集中的存活對象復(fù)制到新的Region上,并為重分配集中的每個(gè)Region維護(hù)一個(gè)轉(zhuǎn)發(fā)表(Forward Table),記錄從舊對象到新對象的轉(zhuǎn)向關(guān)系。ZGC收集器能僅從引用上就明確得知一個(gè)對象是否處于重分配集之中,如果用戶線程此時(shí)并發(fā)訪問了位于重分配集中的對象,這次訪問將會被預(yù)置的內(nèi)存屏障所截獲,然后立即根據(jù)Region上的轉(zhuǎn)發(fā)表記錄將訪問轉(zhuǎn)發(fā)到新復(fù)制的對象上,并同時(shí)修正更新該引用的值,使其直接指向新對象,ZGC將這種行為稱為指針的“自愈”(Self-Healing)能力。
ZGC的染色指針因?yàn)椤白杂?#xff08;Self-Healing)能力,所以只有第一次訪問舊對象會變慢,而Shenandoah的Brooks轉(zhuǎn)發(fā)指針是每次都會變慢。一旦重分配集中某個(gè)Region的存活對象都復(fù)制完畢后,這個(gè)Region就可以立即釋放用于新對象的分配,但是轉(zhuǎn)發(fā)表還得留著不能釋放掉,因?yàn)榭赡苓€有訪問在使用這個(gè)轉(zhuǎn)發(fā)表。
· 并發(fā)重映射(Concurrent Remap):重映射所做的就是修正整個(gè)堆中指向重分配集中舊對象的所有引用,但是ZGC中對象引用存在“自愈”功能,所以這個(gè)重映射操作并不是很迫切。ZGC很巧妙地把并發(fā)重映射階段要做的工作,合并到了下一次垃圾收集循環(huán)中的并發(fā)標(biāo)記階段里去完成,反正它們都是要遍歷所有對象的,這樣合并就節(jié)省了一次遍歷對象圖的開銷。
ZGC存在的問題
ZGC最大的問題是浮動垃圾。
浮動垃圾
ZGC的停頓時(shí)間是在10ms以下,但是ZGC的執(zhí)行時(shí)間還是遠(yuǎn)遠(yuǎn)大于這個(gè)時(shí)間的。假如ZGC全過程需要執(zhí)行10分鐘,在這個(gè)期間由于對象分配速率很高,將創(chuàng)建大量的新對象,這些對象很難進(jìn)入當(dāng)次GC,所以只能在下次GC的時(shí)候進(jìn)行回收,這些只能等到下次GC才能回收的對象就是浮動垃圾。
ZGC沒有分代概念,每次都需要進(jìn)行全堆掃描,導(dǎo)致一些“朝生夕死”的對象沒能及時(shí)的被回收。
解決方案
目前唯一的辦法是增大堆的容量,使得程序得到更多的喘息時(shí)間,但是這個(gè)也是一個(gè)治標(biāo)不治本的方案。如果需要從根本上解決這個(gè)問題,還是需要引入分代收集,讓新生對象都在一個(gè)專門的區(qū)域中創(chuàng)建,然后專門針對這個(gè)區(qū)域進(jìn)行更頻繁、更快的收集。
官方測試數(shù)據(jù)
停頓時(shí)間
在ZGC的停頓時(shí)間測試上,和其他收集器相比完全不在一個(gè)數(shù)量級,如圖:
吞吐量
ZGC的“弱項(xiàng)”吞吐量方面,以低延遲為首要目標(biāo)的ZGC已經(jīng)達(dá)到了以高吞吐量為目標(biāo)Parallel Scavenge的99%,直接超越了G1,如圖:
優(yōu)缺點(diǎn)
· 優(yōu)點(diǎn):低停頓,高吞吐量,ZGC收集過程中額外耗費(fèi)的內(nèi)存小
· 缺點(diǎn):浮動垃圾
總結(jié)
以上是生活随笔為你收集整理的JDK 13 的最新垃圾回收器ZGC,你了解多少?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十分钟了解 git 那些 “不常用” 命
- 下一篇: 后端程序员必备:书写高质量SQL的30条