G1從入門(mén)到放棄(二)
上一篇文章主要講了G1的理論知識(shí),本篇文章會(huì)講解在實(shí)際生產(chǎn)中如何讀懂G1日志,以及介紹G1的參數(shù)配置。
Young GC日志
通過(guò)使用-XX:+PrintGCDetails
參數(shù)查看的Young GC日志如下:
① 四個(gè)關(guān)鍵信息
-
2016-12-12T10:40:18.811-0500:GC發(fā)生的時(shí)間(通過(guò)設(shè)置
-XX:+PrintGCDateStamps
打印) - 29.959:相對(duì)JVM啟動(dòng)的時(shí)間
- G1 Evacuation Pause (young):GC類(lèi)型,表示這是evacuation停頓,并且是Young GC。
- 0.0305171 sec:本次GC耗時(shí)。
② 所有并行任務(wù)
- Parallel Time:26.6 。并行任務(wù)花費(fèi)的STW的時(shí)間,從收集開(kāi)始到最后一個(gè)GC線程結(jié)束。
- GC Workers:4 。并行收集的線程數(shù)量。通過(guò) -XX:ParallelGCThreads。當(dāng)CPU數(shù)量小于8時(shí),該值為CPU個(gè)數(shù),最大設(shè)置成8,對(duì)于多于8個(gè)的CPU,將默認(rèn)取CPU個(gè)數(shù)的5/8。
- GC Worker start:最小|最大時(shí)間戳表示第一個(gè)線程和最后一個(gè)線程的啟動(dòng)時(shí)間。理想情況下希望同時(shí)啟動(dòng)。
- **Ext Root Scanning **:掃描外部根節(jié)點(diǎn)的時(shí)間。外部節(jié)點(diǎn)包括JNI、全局變量、線程棧等。
- **Update RS (Remembered Set or RSet) **:每個(gè)線程更新RSet的時(shí)間。
- **Scan RS **: 掃描每個(gè)CSet中Region的RSet,避免了掃描整個(gè)老年代。
- Code Root Scanning:掃描code root耗時(shí)。Code Root是JIT編譯后的代碼里引用了heap中的對(duì)象。
- Object Copy:拷貝存活對(duì)象到新的Region.
- Termination: 當(dāng)GC線程完成任務(wù)之后嘗試結(jié)束到真正結(jié)束耗時(shí)。因?yàn)樵诮Y(jié)束前他會(huì)檢查其他線程是否有未完成的任務(wù),幫助完成之后再結(jié)束。
- GC Worker Other:線程花費(fèi)在其他工作上的時(shí)間
- GC Worker Total:每個(gè)線程花費(fèi)的時(shí)間總和。
- GC Worker End: 每個(gè)線程的結(jié)束時(shí)間。最小|最大時(shí)間戳表示第一個(gè)線程和最后一個(gè)線程的結(jié)束時(shí)間。理想情況下希望同時(shí)結(jié)束。
③ 串行任務(wù)
- Code Root Fixup:修復(fù)GC期間code root指針改變的耗時(shí)
- Code Root Purge:清除code root耗時(shí)
- Clear CT:清除card tables 中的dirty card的耗時(shí)
④其他事項(xiàng)
其他事項(xiàng)共耗時(shí)3.7ms,其他事項(xiàng)包括選擇CSet,處理已用對(duì)象,引用入ReferenceQueues,釋放CSet中的region。
⑤各代變化
- Eden: 1097.0M(1097.0M)->0.0B(967.0M):表明了Young GC被觸發(fā),因?yàn)镋den區(qū)已經(jīng)滿了(分配了1097M 已經(jīng)使用了1097.0M),并且Eden區(qū)都被清空了(0B),下次垃圾回收Eden區(qū)大小降到967M。
- Survivors: 13.0M->139.0M:Young GC之后,Survivor從13M增加到了139M
- Heap: 1694.4M(2048.0M)->736.3M(2048.0M):開(kāi)始前整個(gè)堆占用了1694.4M,最大可分配2048M,在收集之后,整個(gè)堆占用736M,最大可分配沒(méi)有變還是2048M。
⑥ 這次回收耗時(shí)
- user=0.08:在垃圾回收時(shí),花費(fèi)在用戶(hù)代碼上的CPU時(shí)間。這個(gè)時(shí)間包含了所有線程運(yùn)行的CPU時(shí)間,所以比real-time大很多
- sys=0.00: 花費(fèi)在系統(tǒng)內(nèi)核上的時(shí)間。
- real=0.03: 垃圾回收的實(shí)際時(shí)間。這里包括了其他進(jìn)程的時(shí)間和等待時(shí)間。
并發(fā)日志
①標(biāo)明標(biāo)記階段開(kāi)始
-
GC pause (G1 Evacuation Pause) (young) (initial-mark):利用STW停頓期間,跟蹤所有可達(dá)對(duì)象,該階段和Young GC一起執(zhí)行。同時(shí)該階段也設(shè)置兩個(gè)指針TAMS來(lái)標(biāo)識(shí)已經(jīng)存在的對(duì)象以及在并發(fā)標(biāo)記階段新生成的對(duì)象,這兩個(gè)指針在上一篇文章中介紹過(guò)。
② 第一個(gè)并發(fā)事件 - GC concurrent-root-region-scan-start: 掃描初始化標(biāo)記階段Survivor區(qū)的root Region并標(biāo)記出來(lái)。
③并發(fā)標(biāo)記
-
GC concurrent-mark-start:該階段和應(yīng)用線程一起執(zhí)行,并發(fā)線程數(shù)默認(rèn)是并行線程數(shù)的四分之一。可以通過(guò)
-XX:ConcGCThreads
顯示指定。
④ STW階段
- GC remark / Finalize Marking / GC ref-proc / Unloading: 這個(gè)階段
⑤ 這也是STW階段
-GC cleanup: 這個(gè)階段沒(méi)有存活對(duì)象的Old Region和Humongous Region將被釋放和清空。為了準(zhǔn)備下次GC,在CSets中的Old Regions會(huì)根據(jù)他們的回收收益的大小排序。為了準(zhǔn)備下一次標(biāo)記,previous bitmaps 和 next bitmaps會(huì)被交換。同時(shí)并行線程會(huì)標(biāo)記那些inital mark階段生成的對(duì)象,以及至少存在一個(gè)存活對(duì)象的region的bitmap。
⑥這也是一個(gè)并發(fā)階段
- GC concurrent-cleanup-start:處理第5階段所有空的Region。每個(gè)Region中的RSet被清空,當(dāng)所有的Region都被清理完成,他們會(huì)被加入到一個(gè)臨時(shí)表中,最終會(huì)被合并到master free list。
Mixed GC
當(dāng)并發(fā)標(biāo)記完成后,在Young GC日志后面緊隨著Mixed GC,下面是Mixed GC日志。可以看到Mixed GC日志和前面介紹的Young GC很相似,只有兩個(gè)不同點(diǎn):
1、第一行會(huì)表示這是一個(gè)Mixed GC
2、收集的集合里包含了老年代(Old Region),由并發(fā)標(biāo)記階段確定的。
Full GC
Full GC的日志結(jié)果如下。
需要注意的是如果是幾天一次Full GC,則是正常現(xiàn)象,但是每小時(shí)頻繁GC就需要調(diào)優(yōu)了。
其他
建議大家開(kāi)啟-XX:+PrintAdaptiveSizePolicy
和-XX:+PrintTenuringDistribution
兩個(gè)標(biāo)簽,可以幫助大家更好的分析日志。
-
-XX:+PrintAdaptiveSizePolicy
: 顯示收集器工效(Collector ergonomics) -
-XX:+PrintTenuringDistribution
: Survivor區(qū)的使用和分布
Young GC開(kāi)啟-XX:+PrintAdaptiveSizePolicy
之后的日志如下:
① 告訴我們有多少在dirty card隊(duì)列里的cards等待被處理。并且展示了預(yù)計(jì)處理時(shí)間(包括了更新RSet和掃描RSet的時(shí)間);
② 有多少Region將被加入到這次GC中
③ 選擇出CSets并且估算這次收集時(shí)間
④ 該行并不一定在Young GC日志中出現(xiàn),如果花費(fèi)在GC上的時(shí)間比應(yīng)用線程大到一個(gè)閾值的時(shí)候,G1可以動(dòng)態(tài)擴(kuò)大堆大小。如果你設(shè)置了最大堆和最小堆的大小相等,該行不會(huì)出現(xiàn)
⑤ 當(dāng)并發(fā)標(biāo)記開(kāi)始時(shí)出現(xiàn)。
Young GC后面是并發(fā)回收日志。
Young GC日志中還可能存在關(guān)于Mixed GC的日志:
①告訴我們Mixed GC開(kāi)始,原因是可回收垃圾百分比(22.62%)大于了我們的閾值(5%)。
下面是Mixed GC開(kāi)啟-XX:+PrintAdaptiveSizePolicy
之后執(zhí)行日志
① 該階段包括CSet和一部分Young Region的選擇
②描述Mixed GC時(shí),Old Region被加入到CSet中。默認(rèn)情況下,G1只把10%的Old Region加入到CSet中,通過(guò)配置-XX:G1OldCSetRegionThresholdPercent=X
可以更改
③提供最終的CSet和停頓預(yù)測(cè)
④描述Mixed GC狀態(tài)細(xì)節(jié)。在這個(gè)案例中,我們?nèi)匀挥?35個(gè)Old Region可以被回收,大約305363768字節(jié),占整個(gè)堆大小的14.22%。由于仍然大于閾值,下個(gè)階段回收仍然是Mixed GC。
下面是Full GC開(kāi)啟-XX:+PrintAdaptiveSizePolicy
之后執(zhí)行日志
① 沒(méi)有空的Region用來(lái)分配對(duì)象,請(qǐng)求擴(kuò)容堆
② 擴(kuò)容需要多少空間。到目前為止還沒(méi)有真正執(zhí)行擴(kuò)容。
③ 不會(huì)嘗試擴(kuò)容。因?yàn)闆](méi)有可用的Region,所有要執(zhí)行Full GC。
④ 在最小堆小于最大堆時(shí)出現(xiàn)的日志。G1 在一次Full GC后,嘗試縮小堆到70%。這個(gè)百分比可以通過(guò)-XX:InitiatingHeapOccupancyPercent
(IHOP)調(diào)節(jié),這個(gè)參數(shù)設(shè)置使用整個(gè)對(duì)的x%時(shí),系統(tǒng)開(kāi)始進(jìn)行并行GC。注意是整個(gè)堆的百分比。
⑤ 堆正在被縮小,已經(jīng)縮小了多少容量。
-XX:+PrintTenuringDistribution
: 可以查看每次回收期間,Survivor區(qū)的分布信息。可以幫助我們查看對(duì)象年齡的變化。
上圖主要從三個(gè)層面展示Survivor區(qū):
-desired survivor size: 期望的Survivor大小。該值等于Survivor大小乘以TargetSurvivorRatio (默認(rèn)50%)。
- target threshold:目標(biāo)閾值。表示一個(gè)對(duì)象的年齡,這個(gè)值可以通過(guò)每個(gè)年齡的所有對(duì)象大小相加直到大于desired survivor size計(jì)算出來(lái)。
- age distribution: 年齡分布。包括了每個(gè)年齡所有對(duì)象的大小以及增量Survivor區(qū)大小