java g1 gc ref proc_深入理解垃圾收集器的G1及日志分析
盡管Hotspot 最新的垃圾回收器G1是在2006年推出的。但是G1從推行至今的市場反響來看,但現(xiàn)在足以證明這款垃圾收集器是經(jīng)得起考驗(yàn)的,從java9開始,就默認(rèn)為G1垃圾收集器。G1是一款面向服務(wù)端應(yīng)用的垃圾收集器。HotSpot開發(fā)團(tuán)隊(duì)賦予它的使命是(在比較長期的)未來可以替換掉JDK1.5中發(fā)布的CMS收集器。與其他GC收集器相比,G1具備如下特點(diǎn)。
并行與并發(fā)、分代收集的垃圾收集算法、可預(yù)測的停頓、空間整合。
特點(diǎn)
從分代來看,G1依然屬于分代垃圾收集器,她會區(qū)分年輕代和老年代,依然有eden區(qū)和survivor區(qū),但從堆的結(jié)構(gòu)來看,它并不要求整個(gè)eden、年輕代或者老年代都連續(xù),它使用了分區(qū)算法。
并行性: 在回收期間,可以由多個(gè)GC線程同時(shí)工作,有效利用多核計(jì)算能力。
井發(fā)性: GI 擁有與應(yīng)用程序交替執(zhí)行的能力,部分工作可以和應(yīng)用程序同時(shí)執(zhí)行,因此一 般來說,不會在整個(gè)回收期間完全阻塞應(yīng)用程序。
分代 GC : Gl 依然是一個(gè)分代收集器,但是和之前回收器不同,它同時(shí)兼顧年輕代和老年代。對比其他回收器,它們或者工作在年輕代,或者工作在老年代。因此,這里是一個(gè)很大的不同。
空間整理: Gl 在回收過程中,會進(jìn)行適當(dāng)?shù)膶ο笠苿?#xff0c;不像CMS,只是簡單地標(biāo)記清理對象,在若干次 GC 后,CMS 必須進(jìn)行一次碎片整理。而Gl不同,它每次回收都會有效地復(fù)制對象,減少空間碎片。
G1把內(nèi)存“化整為零”的思路,理解起來似乎很容易,但其中的實(shí)現(xiàn)細(xì)節(jié)卻遠(yuǎn)遠(yuǎn)沒有想象中那樣簡單,否則也不會從2004年Sun實(shí)驗(yàn)室發(fā)表第一篇G1的論文開始直到今天(將近10年時(shí)間)才開發(fā)出G1的商用版。
筆者以一個(gè)細(xì)節(jié)為例:把Java堆分為多個(gè)Region后,垃圾收集是否就真的能以Region為單位進(jìn)行了?聽起來順理成章,再仔細(xì)想想就很容易發(fā)現(xiàn)問題所在:Region不可能是孤立的。一個(gè)對象分配在某個(gè)Region中,它并非只能被本Region中的其他對象引用,而是可以與整個(gè)Java堆任意的對象發(fā)生引用關(guān)系。那在做可達(dá)性判定確定對象是否存活的時(shí)候,豈不是還得掃描整個(gè)Java堆才能保證準(zhǔn)確性?這個(gè)問題其實(shí)并非在G1中才有,只是在G1中更加突出而已。在以前的分代收集中,新生代的規(guī)模一般都比老年代要小許多,新生代的收集也比老年代要頻繁許多,那回收新生代中的對象時(shí)也面臨相同的問題,如果回收新生代時(shí)也不得不同時(shí)掃描老年代的話,那么Minor GC的效率可能下降不少。
?在G1收集器中,Region之間的對象引用以及其他收集器中的新生代與老年代之間的對象引用,虛擬機(jī)都是使用Remembered Set來避免全堆掃描的。G1中每個(gè)Region都有一個(gè)與之對應(yīng)的Remembered Set,虛擬機(jī)發(fā)現(xiàn)程序在對Reference類型的數(shù)據(jù)進(jìn)行寫操作時(shí),會產(chǎn)生一個(gè)Write Barrier暫時(shí)中斷寫操作,檢查Reference引用的對象是否處于不同的Region之中(在分代的例子中就是檢查是否老年代中的對象引用了新生代中的對象),如果是,便通過CardTable把相關(guān)引用信息記錄到被引用對象所屬的Region的RememberedSet之中。當(dāng)進(jìn)行內(nèi)存回收時(shí),在GC根節(jié)點(diǎn)的枚舉范圍中加入Remembered Set即可保證不對全堆掃描也不會有遺漏。
G1的內(nèi)存劃分和主要收集過程
G1收集器講堆進(jìn)行分區(qū),劃分為一個(gè)個(gè)區(qū)域,每次收集時(shí),只收集其中幾個(gè)區(qū)域,以此來控制垃圾回收產(chǎn)生的一次停頓時(shí)間。
G1收集過程四個(gè)階段:
新生代GC(YGC)
并發(fā)標(biāo)記周期
混合收集
如果需要進(jìn)行full GC
G1的新生代GC
新生代GC的主要工作是回收eden區(qū)和survivor區(qū)。 一旦eden 區(qū)被占滿,新生代GC就會啟動。新生代GC收集前后的堆數(shù)據(jù)如圖5.6所示,其中E表示eden區(qū),S表示survivor區(qū), o表示老年代。可以看到,新生代GC只處理eden和survivor區(qū),回收后,所有的eden區(qū)都應(yīng)該被消空,而survivor區(qū)會被收集一 部分?jǐn)?shù)據(jù),但是應(yīng)該至少仍然存在一 個(gè) survivor 區(qū),類比其他的新生代收集器,這一 點(diǎn)似乎并沒有太大變化。另一 個(gè)亟要的變化是老年代的區(qū)域增多,因?yàn)椴糠謘urvivor區(qū)或者eden區(qū)的對象可能會晉升到老年代。
從日志中可以看到,eden區(qū)原本占用235MB空間,回收后被清空,survivor區(qū)從5MB增長到了11MB, 這是因?yàn)椴糠謱ο髲膃den區(qū)復(fù)制到survivor區(qū),整個(gè)堆合計(jì)為400MB, 從回收前的239MB下降到10.5MB。
G1的井發(fā)標(biāo)記周期
G1的并發(fā)階段與CMS有點(diǎn)類似,他們都是為了降低一次停頓時(shí)間,而將可以和應(yīng)用程序并發(fā)的部分單獨(dú)提取出來執(zhí)行。
并發(fā)標(biāo)記周期可以分為以下幾步。如果不計(jì)算維護(hù)RememberedSet的操作,G1收集器的運(yùn)作大致可劃分為以下幾個(gè)步驟:
初始標(biāo)記(Initial Marking)
根區(qū)域掃描
并發(fā)標(biāo)記(Concurrent Marking)
最終標(biāo)記(Final Marking)
獨(dú)占清理
篩選回收(Live Data Counting and Evacuation)
復(fù)制代碼
初始標(biāo)記: 標(biāo)記從根節(jié)點(diǎn)直接可達(dá)的對象。這個(gè)階段會伴隨一次新生代GC,它會產(chǎn)生全局停頓。
根區(qū)域掃描: 由于初始標(biāo)記必然會伴隨一次新生代的GC,所以在初始化標(biāo)記后,eden被清空,并且存活對象被移入survivor區(qū)。這個(gè)階段,將掃面survivor區(qū)直接可達(dá)的老年代對象,并標(biāo)記這些直接可達(dá)的對象。根區(qū)域掃描不能和新生代GC同時(shí)執(zhí)行。
并發(fā)標(biāo)記: 和CMS類似,掃面查找整個(gè)對存活的對象,這是一個(gè)并發(fā)的過程,可以被一次新生代GC打斷。
重新標(biāo)記: 由于并發(fā)標(biāo)記過程中,應(yīng)用仍在執(zhí)行,因此標(biāo)記結(jié)果需要修正,所以對上一次的標(biāo)記結(jié)果進(jìn)行補(bǔ)充,在G1中,這個(gè)過程使用STAB算法完成。即G1會在標(biāo)記之初為存活對象創(chuàng)建一個(gè)快照,有助于加速重新標(biāo)記速度。
獨(dú)占清理: 這個(gè)階段會引起停頓。
并發(fā)清理階段: 識別并清理完全空閑的區(qū)域。它是并發(fā)的清理,不會引起停頓。
在并發(fā)標(biāo)記周期時(shí),G1會產(chǎn)生如下日志:
(1)、初始標(biāo)記,伴隨一次新生代GC
[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0117414 secs]
...
[Eden: 1024.0K(4096.0K)->0.0B(2048.0K) Survivors: 2048.0K->1024.0K Heap: 9581.8K(20.0M)->12.3M(20.0M)]
[Times: user=0.11 sys=0.00, real=0.01 secs]
復(fù)制代碼
可以看到初始化標(biāo)記時(shí),eden被清空,并部分復(fù)制到survivor區(qū)
(2)、這是一次并發(fā)的根區(qū)域掃描,并發(fā)掃面過程中,不能被新生代GC打斷。
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0007368 secs]
復(fù)制代碼
(3)、下面這個(gè)是指并發(fā)標(biāo)記
[GC concurrent-mark-start]
[GC pause (G1 Evacuation Pause) (young) (to-space exhausted), 0.0427113 secs]
.....
[Eden: 2048.0K(2048.0K)->0.0B(1024.0K) Survivors: 1024.0K->1024.0K Heap: 16.4M(20.0M)->18.0M(20.0M)]
[Times: user=0.05 sys=0.02, real=0.04 secs]
...
[GC concurrent-mark-abort]
復(fù)制代碼
(4)、重新標(biāo)記引起全局停頓
[Ref Proc: 0.3 ms]
復(fù)制代碼
(5)、重新標(biāo)記后進(jìn)行獨(dú)占清理
4.088: [GC cleanup 117M->106M(138M), 0.0015198 secs]
復(fù)制代碼
(6)、并發(fā)清理是并發(fā)執(zhí)行的,會根據(jù)獨(dú)占清理階段計(jì)算出的每個(gè)區(qū)域的存活對象數(shù)量,直接回收已經(jīng)不包含存活對象的區(qū)域。
4.090: [GC concurrent-cleanup-start]
并發(fā)清理階段開始,它釋放被發(fā)現(xiàn)為空的區(qū)域(不包含任何的活躍數(shù)據(jù)的區(qū)域),在上一個(gè)stop-the-world階段期間。
4.091: [GC concurrent-cleanup-end, 0.0002721]
并發(fā)清理階段清理空的區(qū)域用時(shí)0.0002721秒。
復(fù)制代碼
關(guān)于G1日志,想要知道所有內(nèi)容的,可以看這篇文章,適合查詢:
blog.csdn.net/zhanggang80…
混合回收
在并發(fā)標(biāo)記周期中,雖然有部分對象被回收,但是從整體上來說,回收的比例是相當(dāng)?shù)偷摹5窃诓l(fā)標(biāo)記周期后,G1已經(jīng)明確知道哪些區(qū)域含有比較多的垃圾對象了,在混合階段,就是對這些區(qū)域進(jìn)行回收的。當(dāng)然,會優(yōu)先回收垃圾比例比較高的區(qū)域。因?yàn)榛厥者@些區(qū)域的性價(jià)比比較高。
G1是指垃圾優(yōu)先的垃圾回收器,全稱"Garbage First Garbage Collector".
在混合回收中,即會執(zhí)行正常年輕代GC,也會選取被標(biāo)記的老年代區(qū)域進(jìn)行回收,它同時(shí)處理了新生代和老年代。因?yàn)樾律鶪C的原因,eden區(qū)域必然被清空,此外,如下圖,有兩塊區(qū)域被標(biāo)記為G的垃圾回收比例最高的區(qū)域被清理。被清理區(qū)域的存活對象會被移動到其他區(qū)域,這樣的好處是可以減少空間碎片。
混合GC產(chǎn)生如下日志:
混合GC執(zhí)行多次,直到回收了足夠多的內(nèi)存空間,觸發(fā)一次新生代GC。新生代GC后,有可能會發(fā)生一次并發(fā)標(biāo)記周期的清理,最后又引起混合GC。整個(gè)流程見下圖:
必要時(shí)的Full GC
與CMS類似,并發(fā)收集由于讓應(yīng)用和GC線程交替工作,因此總是不能避免在特別的繁忙場合在回收過程導(dǎo)致內(nèi)存不足,此時(shí),G1也會執(zhí)行一個(gè)Full GC回收。
此外,在混合GC和新生代GC時(shí),survivor與老年代無法無法容納幸存對象,都會導(dǎo)致Full GC產(chǎn)生
完整的G1日志分析
G1 的相關(guān)參數(shù)
對于Gl收集器,可以使用-XX:+UseGIGC標(biāo)記打開Gl收集器開關(guān),對Gt收集器進(jìn)行設(shè)置時(shí),最重要的一 個(gè)參數(shù)就是-XX :MaxGCPauseMillis,它用于指定目標(biāo)最大停頓時(shí)間。如果任何一次停頓超過這個(gè)設(shè)置值時(shí),Gl就會嘗試調(diào)整新生代和老年代的比例、調(diào)整堆大小、調(diào)整晉升年齡等手段,試圖達(dá)到預(yù)設(shè)目標(biāo)。對于性能調(diào)優(yōu)來說,
有時(shí)候,總是魚和熊掌不可兼得的,如果停頓時(shí)間縮短,對千新生代來說,這意味著很可能要增加新生代GC的次數(shù),GC反而會變得更加頻繁。對于老年代區(qū)域來說,為了獲得更短的停頓時(shí)間,那么在混合GC收集時(shí),一次收集的區(qū)域數(shù)量也會變少,這樣無疑增加了進(jìn)行FullGC的可能性。另外一個(gè)重要的參數(shù)是-XX :ParallelGCThreads, 它用于設(shè)置并行回收時(shí),GC的工作線程數(shù)量。
此外,-XX:InitiatingHeapOccupancyPercent參數(shù)可以指定當(dāng)整個(gè)堆使用率達(dá)到多少時(shí),觸發(fā)并發(fā)標(biāo)記周期的執(zhí)行。默認(rèn)值是45, 即當(dāng)整個(gè)堆占用率達(dá)到45%時(shí),執(zhí)行并發(fā)標(biāo)記周期。 InitiatingHeapOccupancyPercent 一 旦設(shè)置,始終都不會被G l收集器修改,這意味著G I收集器不會試圖改變這個(gè)值,來滿足MaxGCPause汕His的目標(biāo)。如果InitiatingHeapOccupancyPercent值設(shè)置偏大,會導(dǎo)致并發(fā)周期遲遲得不到啟動,那么引起Full GC的可能性也大大增加,反之,一 個(gè)過小的 InitiatingHeapOccupancyPercent值,會使得并發(fā)周期非常頻繁,大整 GC 線程搶占CPU, 會導(dǎo)致應(yīng)用程序的性能有所下降。
來自《深入理解JVM虛擬機(jī)》JVM高級特性與最佳實(shí)現(xiàn)。
《實(shí)戰(zhàn)java虛擬機(jī)》復(fù)制代碼
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java g1 gc ref proc_深入理解垃圾收集器的G1及日志分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何理解续航里程
- 下一篇: 汽车展览有哪些文化元素?