G1 GC知識(shí)點(diǎn):
Region:1M~64M,2的冪,默認(rèn)為其大小為將堆分為約2048個(gè)region為宜??梢酝ㄟ^(guò)-XX:G1HeapRegionSize來(lái)設(shè)定region大小,但是不推薦這么做,region過(guò)少會(huì)導(dǎo)致G1的靈活性降低,掃描的時(shí)間增長(zhǎng)。
free list:由空的region組成的linked list
GC 過(guò)程如下:
1.當(dāng)一個(gè)對(duì)象需要分配時(shí),首先從free list里獲取一個(gè)region的TLAB(Thread Local Allocation Buffer)本地線程緩沖區(qū),然后在這個(gè)TLAB上進(jìn)行分配。
2. 以此類(lèi)推,直到所有Eden的region的總量都被填滿,就會(huì)觸發(fā)一次young GC。這里的cumulative amount of Eden space 跟目標(biāo)暫停時(shí)間有關(guān),在堆的5%~60%之間波動(dòng),其大小跟上次young GC的表現(xiàn)有關(guān)。
3. 當(dāng)上述young GC完成后,死亡的對(duì)象被回收,存活的對(duì)象經(jīng)過(guò)疏散和壓縮后拷貝到Survivor區(qū),而年齡超過(guò)閾值(可以通過(guò)MaxTenuringThreshold設(shè)置,默認(rèn)15)的則會(huì)晉升到old區(qū)。
4. 如果滿足如下三種情況之一,G1收集會(huì)繼續(xù)
? ? a. occupancy higher than threshold?達(dá)到the InitiatingHeapOccupancyPercent (IHOP),old regions里分配的對(duì)象大于堆的45%(G1HeapWastePercent參數(shù))
? ? b. candidate old regions available 達(dá)到G1ReservePercent (G1MixedGCLiveThresholdPercent defaults to 85% in JDK8u40+ and 65% in JDK7)
? ? c. 遇到巨型數(shù)據(jù)的分配
5. 并發(fā)標(biāo)記。基于snapshot-at-the-beginning (SATB)標(biāo)記算法,在GC開(kāi)始時(shí)先創(chuàng)建一個(gè)對(duì)象快照,STAB可以在并發(fā)標(biāo)記時(shí)標(biāo)記所有快照中當(dāng)時(shí)的存活對(duì)象。標(biāo)記過(guò)程中新分配的對(duì)象也會(huì)被標(biāo)記為存活對(duì)象,不會(huì)被回收。
6. 當(dāng)并發(fā)標(biāo)記完成后,會(huì)再觸發(fā)一個(gè)young GC,然后再出發(fā)一個(gè)mixed GC。mixed GC與young GC主要有2點(diǎn)不同,mixed GC會(huì)回收包括Eden和old區(qū)的對(duì)象,二者觸發(fā)的條件不一樣。
初級(jí)標(biāo)記(Mandatory):

根據(jù)參數(shù)‘-XX:+PrintGCDetails’,產(chǎn)生如下log

1. 信息總覽:
????a. 通過(guò)設(shè)置?‘-XX:+PrintGCDateStamps’ 得到GC發(fā)生的準(zhǔn)備日期時(shí)間 -?2016-12-12T10:40:18.811-0500
? ? b. JVM啟動(dòng)到當(dāng)前時(shí)間的相對(duì)時(shí)間?-?29.959
? ? c. 收集的類(lèi)型?-?G1 Evacuation Pause (young)
? ? d. 垃圾收集消耗的總時(shí)長(zhǎng)?-?0.0305171 sec?
2. 并行任務(wù):
? ? a.?Parallel Time - 并行任務(wù)STW總時(shí)長(zhǎng)?- 26.6ms
? ? b. GC Workers?- 并行GC workers的總數(shù)量,通過(guò)?"-XX:ParallelGCThreads"設(shè)置 - 4
? ? ? ? i. 當(dāng)cpu內(nèi)核數(shù) <= 8,設(shè)置為cpu核數(shù);>8時(shí),設(shè)置為核數(shù)的5/8
? ? c.?GC Worker Start?- GCworker開(kāi)始工作時(shí)間相對(duì)于JVM啟動(dòng)的最大最小時(shí)間,diff就是第一個(gè)線程啟動(dòng)與最后一個(gè)線程啟動(dòng)時(shí)間之差,這個(gè)差值肯定越小越好
? ? d.?Ext?Root Scanning - 外部根區(qū)(堆外區(qū),線程棧根,JNI,全局變量,系統(tǒng)目錄,classloader等)掃描消耗時(shí)間
? ? e.?Update RS (Remembered Set or RSet) - 在每次開(kāi)始收集之前都要進(jìn)行Rset更新,保證RSet是最新的。-XX:MaxGCPauseMillis參數(shù)是限制G1的暫停時(shí)間,一般RSet更新的時(shí)間小于10%的目標(biāo)暫停時(shí)間是比較可取的。如果花費(fèi)在RSetUpdate的時(shí)間過(guò)長(zhǎng),可以修改其占用總暫停時(shí)間的百分比-XX:G1RSetUpdatingPauseTimePercent。這個(gè)參數(shù)的默認(rèn)值是10。
? ? f.? Scan RS?- 掃描每個(gè)Region的RSet,尋找被待收集集合引用的區(qū)域? ??
? ? g. Code Root Scanning?- 掃描被待收集集合引用的編譯源碼根節(jié)點(diǎn)
? ? h.?Object Copy - 將待收集集合中所有存活的對(duì)象拷貝到新的區(qū)域
? ? i.? Termination?- 當(dāng)一個(gè)GC worker結(jié)束工作后,需要等待其他線程,并嘗試幫其他線程完成為完成的task. 結(jié)束時(shí)間指的就是這個(gè)線程結(jié)束收集到真正結(jié)束的時(shí)間差
? ? j. GC Worker Other?-?花在GC之外的工作線程的時(shí)間,比如因?yàn)镴VM的某個(gè)活動(dòng),導(dǎo)致GC線程被停掉。這部分消耗的時(shí)間不是真正花在GC上,只是作為log的一部分記錄
? ? k.?GC Worker Total - 每個(gè)并行回收線程的時(shí)間統(tǒng)計(jì)
? ? l. GC Worker End?- GC相對(duì)于JVM啟動(dòng)的結(jié)束時(shí)間. diff指第一個(gè)和最后一個(gè)完成的線程之間差值,越小越好
3. 串行任務(wù):
? ? a.?Code Root Fixup - 遍歷那些指向CSet的方法,修正指針
? ? b.?Code Root Purge - 清理code root table
? ? c.?Clear CT?- 清除card table里的臟cards
4. 其他串行操作:
? ? a.?Choose CSet - 選取CSet
? ? b.?Ref Proc - 處理STW引用處理器發(fā)現(xiàn)的soft/weak/final/phantom/JNI引用
? ? c. Ref?Enq?- 將引用排列到相應(yīng)的reference隊(duì)列里
? ? d. Reditry?Cards?- 在回收過(guò)程中被修改的cards標(biāo)記為臟卡
? ? e. Humongous Register?- 在youngGC的時(shí)候會(huì)收集巨型區(qū)域。這個(gè)指標(biāo)是指評(píng)估巨型區(qū)域是否足夠記錄的時(shí)間。
? ? f.?Humongous Reclaim - 確認(rèn)巨型對(duì)象死亡并清理,釋放巨型對(duì)象區(qū)域,重置區(qū)域類(lèi)型,將該區(qū)域放回空閑隊(duì)列所用的時(shí)間
? ? g. Free CSet?-?釋放CSet,其中也會(huì)清理CSet中的RSet,將其放回空閑隊(duì)列
5. 各個(gè)代的變化統(tǒng)計(jì)
? ? a.?Eden: 1097.0M(1097.0M)->0.0B(967.0M)
? ? ? ? i. 該次Young GC被觸發(fā)是因?yàn)镋den區(qū)滿了
? ? ? ? ii. Eden區(qū)通過(guò)該次垃圾回收被清空,變?yōu)?.0B
? ? ? ? iii. Eden區(qū)的總?cè)萘孔兓?097M -> 967M
? ? b.?Survivors: 13.0M->139.0M
? ? ? ? i. Survivor space從13M增長(zhǎng)到139M
? ? c.?Heap: 1694.4M(2048.0M)->736.3M(2048.0M)
? ? ? ? i. 收集的時(shí)候,整個(gè)堆總量為2048M,被使用了1694M
? ? ? ? ii. 回收完畢,整個(gè)堆總量為2048M,被使用了736.3M
6. 垃圾回收花費(fèi)的時(shí)間
? ? a.?user=0.08 - 在回收過(guò)程中花費(fèi)在用戶代碼上的CPU時(shí)間,是所有thread在所有CPU上的花費(fèi)時(shí)間之和。并沒(méi)有計(jì)算處理器之外花費(fèi)的時(shí)間和等待時(shí)間
? ? b.?sys=0.00 -?在回收過(guò)程中花費(fèi)在內(nèi)核處理上的CPU時(shí)間,是所有thread在所有CPU上的花費(fèi)時(shí)間之和。并沒(méi)有計(jì)算處理器之外花費(fèi)的時(shí)間和等待時(shí)間
? ? c.?real=0.03 - 從垃圾回收到結(jié)束的真實(shí)時(shí)間,包括其他處理器花費(fèi)的時(shí)候及等待時(shí)間
并發(fā)標(biāo)記的分析,并發(fā)標(biāo)記可以由不同的方式觸發(fā),但是它的表現(xiàn)是一致的。

1. 標(biāo)記開(kāi)始:
? ? GC pause (G1 Evacuation Pause) (young) (initial-mark)
? ? 標(biāo)記GC Roots,會(huì)STW,尋找所有可達(dá)到的對(duì)象,初始標(biāo)記階段是Young GC的一部分。初始標(biāo)記階段設(shè)置2種TAMS變量來(lái)區(qū)分現(xiàn)存的對(duì)象和并行標(biāo)記時(shí)才分配的對(duì)象。上述被標(biāo)記的對(duì)象被認(rèn)為是存貨的對(duì)象。
2. 第一次并發(fā)事件:
? ? GC concurrent-root-region-scan-start /?GC concurrent-root-region-scan-end
? ? 根分區(qū)掃描,這個(gè)階段GC的線程可以和應(yīng)用線程并發(fā)運(yùn)行。其主要掃描初始標(biāo)記以及之前YoungGC對(duì)象轉(zhuǎn)移到的Survivor分區(qū),并標(biāo)記Survivor區(qū)中引用的對(duì)象。
3. 并發(fā)標(biāo)記
????GC concurrent-mark-start /?GC concurrent-mark-end
? ? a. 跟應(yīng)用線程同時(shí)運(yùn)行,并發(fā)標(biāo)記的線程數(shù)默認(rèn)為parallel thread的25%,也可以通過(guò)”-XX:ConcGCThreads” 設(shè)置
? ? b.?會(huì)并發(fā)標(biāo)記所有非完全空閑的分區(qū)的存活對(duì)象,也即使用了SATB算法,標(biāo)記各個(gè)分區(qū)
4. 最終標(biāo)記
? ??GC remark [ Finalize Marking / GC ref-proc / Unloading]
? ? 有STW,主要處理SATB緩沖區(qū),以及并發(fā)標(biāo)記階段未標(biāo)記到的漏網(wǎng)之魚(yú)(存活對(duì)象)
5.?清除階段
? ? ?GC cleanup 有STW
? ? a. 每個(gè)區(qū)域分別統(tǒng)計(jì)存活對(duì)象。在card bitmap標(biāo)記初始標(biāo)記之后分配的對(duì)象,在Region bitmap標(biāo)記有存貨對(duì)象的區(qū)域
? ? b. 交換bitmaps,為下一次標(biāo)記做準(zhǔn)備
? ? c. 釋放和清理死去的老年區(qū)域和沒(méi)有存貨數(shù)據(jù)的巨型數(shù)據(jù)區(qū)域
? ? d. 清除沒(méi)有存活對(duì)象的區(qū)域的RSet
? ? e. 將老年區(qū)域根據(jù)存活率進(jìn)行排序
? ? f.? 并發(fā)的將無(wú)效的類(lèi)從metaspace卸載
6. 并發(fā)清除
? ??GC concurrent-cleanup-start /?GC concurrent-cleanup-end
? ? a. 清理RSet,包括card cache和code root table
? ? b. 當(dāng)區(qū)域完全清除后,添加到臨時(shí)隊(duì)列,當(dāng)清除結(jié)束后,將臨時(shí)隊(duì)列合并到第二空閑區(qū)域隊(duì)列,等待被添加到主空閑隊(duì)列
當(dāng)并發(fā)標(biāo)記結(jié)束,Young GC后會(huì)跟緊一個(gè)Mixed GC。Mixed GC跟Young GC只有2點(diǎn)不同,如下:

1. 收集類(lèi)型為混合的:?GC pause (G1 Evacuation Pause) (mixed)
2. CSet會(huì)包含通過(guò)并發(fā)標(biāo)記確定的老年區(qū)域
最后一種可能遇到的GC為Full GC,F(xiàn)ull GC是一個(gè)單線程的有STW的過(guò)程。

1. GC 產(chǎn)生原因,(Allocation Failure)。
2. Full GC的頻率
3. Full GC的消耗時(shí)間
加入‘-XX:+PrintGCApplicationStoppedTime’ and ‘-XX:+PrintGCApplicationConcurrentTime’配置產(chǎn)生的log

1. 業(yè)務(wù)線程在安全點(diǎn)停止的時(shí)間
2. 將所有線程帶到安全點(diǎn)并暫停的耗時(shí)
3. 業(yè)務(wù)線程在兩個(gè)安全點(diǎn)之間運(yùn)行的時(shí)間
高級(jí)標(biāo)記(Advanced)

‘-XX:+PrintAdaptiveSizePolicy’:增加G1 ergonomics,便于對(duì)CSet選擇和暫停時(shí)間的估算有更深入的了解。
對(duì)于Young GC,新增了如下log:

1. dirty card queue 有多少cards待處理,并預(yù)估時(shí)間
2. 該次收集有多少region參與,并預(yù)估時(shí)間,并預(yù)估對(duì)象拷貝的時(shí)間
3. 最終CSet的選擇,并預(yù)估總時(shí)間,其實(shí)就是前兩部之和
4. 不一定出現(xiàn)。如果GC線程執(zhí)行時(shí)間/業(yè)務(wù)線程執(zhí)行時(shí)間大于某個(gè)閾值,G1會(huì)嘗試增大堆。不過(guò)我們的設(shè)置都是max=min,不會(huì)出現(xiàn)這個(gè)
5. 如果需要并發(fā)標(biāo)記會(huì)有這行l(wèi)og。
如果有并發(fā)階段,會(huì)多打印如下log:

當(dāng)標(biāo)記結(jié)束,緊著是mixed GC,多增如下log:

開(kāi)始mixed GC的原因,可回收的old區(qū)域>閾值。如果可回收的小于閾值,則不會(huì)開(kāi)始。
Mixed GC開(kāi)始:

1. 選擇CSet和為CSet添加young region跟Young GC一樣
2. 將Old Region添加到CSet。添加的old region的數(shù)量由參數(shù)XX:G1OldCSetRegionThresholdPercent=X控制,默認(rèn)為10%
3. 總結(jié)CSet的情況,并預(yù)測(cè)總的暫停時(shí)間
4. 展示了Mixed GC循環(huán)的詳細(xì)信息。由于釋放后,任然占堆的14%,大于閾值5%,所以下一次垃圾回收還是Mixed
下一次Mixed垃圾回收跟上面的一致,但是結(jié)尾處有點(diǎn)區(qū)別:

1. 由于這次混合垃圾回收之后,old region的占比小于閾值,故下次垃圾回收為young gc
最后,看一下Full GC的ergonomics

1. 在主從空閑隊(duì)列中均沒(méi)有空閑區(qū)域了,分配請(qǐng)求失敗,需要請(qǐng)求堆擴(kuò)展。
2. 堆擴(kuò)展請(qǐng)求。 但是1.2兩步中的操作還沒(méi)真正實(shí)施
3. 對(duì)擴(kuò)展不會(huì)嘗試,由于可用的未提交的regions數(shù)量為0。由于擴(kuò)展失敗,所以開(kāi)始Full GC
4. 由于堆的最小值小于堆的最大值,G1會(huì)在Full GC之后縮小堆的總量至70%
5. 堆縮小的詳細(xì)信息
-XX:+PrintTenuringDistribution 這個(gè)標(biāo)簽提供了survivor空間的分布

任期分布數(shù)據(jù)主要告訴我們survivor空間如下三點(diǎn)信息:
? ? a. The desired survivor size 就是survivor空間總量 乘以?TargetSurvivorRatio (默認(rèn)值為 50%)
? ? b.?The target threshold 就是對(duì)象在Young GC時(shí)存活的歲數(shù)。
? ? c. 年齡的分布,包括不同年齡對(duì)象的大小及增量的總和。

問(wèn)題診斷標(biāo)記(Debug)

‘-XX:+G1PrintHeapRegions’:?

調(diào)試如下問(wèn)題時(shí)有必要打印堆區(qū)事件:
? ? a. 調(diào)試尋找疏散失敗的原因及失敗區(qū)域的編號(hào)
? ? b. 確定巨型對(duì)象的大小和出現(xiàn)頻率
? ? c. 跟蹤和估算被規(guī)劃為CSet的Eden、Survivor和Old區(qū)的數(shù)量
1. COMMIT
? ? 堆的初始化或擴(kuò)展完成,確定區(qū)域的頂和底
2.?ALLOC(Eden)
? ? 分配的Eden區(qū)域,由底的地址確定
3.?CSET
? ? CSet區(qū)域,該區(qū)域?qū)⒈换厥?,由底的地址確定
4. CLEANUP
? ? 在并發(fā)標(biāo)記的時(shí)候完全被清空的區(qū)域,由底的地址確定
5.?UNCOMMIT
? ? 在Full GC后,如果堆被縮小,就會(huì)出現(xiàn)很多未提交的區(qū)域
6. ALLOC(Old)
????分配的Old區(qū)域,由底的地址確定? ??
7. RETIRE
? ? 在垃圾回收結(jié)束的時(shí)候,最后一個(gè)被分配的Old區(qū)會(huì)被標(biāo)記為退休的
8. REUSE
? ? 下次一GC開(kāi)始時(shí),上一次退休的Old區(qū)會(huì)作為起始點(diǎn)
9. ALLOC(Survivor)
????分配的Survivor區(qū)域,由底的地址確定?? ??
10.?EVAC-FAILURE
? ? 如果分配中出現(xiàn)疏散失敗,這里會(huì)指出失敗的區(qū)域
11.?POST-COMPACTION(Old)
? ? 在Full GC結(jié)束后,對(duì)有存活數(shù)據(jù)的Old和巨型數(shù)據(jù)區(qū)會(huì)進(jìn)行壓縮
12.?ALLOC(SingleH)
????分配SingleH巨型數(shù)據(jù)區(qū)域,對(duì)象只能占用一個(gè)區(qū)域
13.?ALLOC(StartsH)
? ? 分配StartsH巨型數(shù)據(jù)區(qū),對(duì)象可以放置在不止一個(gè)區(qū)域中
14.?ALLOC(ContinuesH)
? ? 分配ContinuesH巨型數(shù)據(jù)區(qū)
?‘-XX:+G1PrintRegionLivenessInfo’ 用于分析并發(fā)標(biāo)記后old區(qū)的分布:

其實(shí)就是翻譯了下這篇文章