摘要:原文出處設(shè)計(jì)的一個(gè)重要目標(biāo)是設(shè)置階段的持續(xù)時(shí)長(zhǎng)和頻率,因?yàn)槔占骺深A(yù)測(cè),可配置。收集器盡自己最大努力高概率實(shí)現(xiàn)目標(biāo)但不是必然,它會(huì)是硬實(shí)時(shí)。因此名稱是收集器。運(yùn)行不同使用獨(dú)立的收集器。
原文出處:G1 – Garbage First
G1設(shè)計(jì)的一個(gè)重要目標(biāo)是設(shè)置stop-the-world階段的持續(xù)時(shí)長(zhǎng)和頻率,因?yàn)槔占骺深A(yù)測(cè),可配置。事實(shí)上,G1是一款軟實(shí)時(shí)的收集器,意味著你可以給它設(shè)置明確的運(yùn)行目標(biāo)。你可以要求stop-the-world階段不超過 x milliseconds在給定的y milliseconds時(shí)長(zhǎng)范圍之內(nèi),比如,在給定的s內(nèi)不超過5s。G1收集器盡自己最大努力高概率實(shí)現(xiàn)目標(biāo)(但不是必然,它會(huì)是硬實(shí)時(shí))。
為了實(shí)現(xiàn)它,G1建立在一系列的觀察上。首先,heap去不是必須Young和Old代分配連續(xù)的空間。相反,heap區(qū)分成一定數(shù)量(代表性的2048)的小的heap區(qū)來分配對(duì)象。單個(gè)的區(qū)域可能是Eden區(qū),Survivor區(qū),Old區(qū)。所有邏輯的Eden區(qū)和Survivor區(qū)合稱為Young代,所有的Old區(qū)組合在一起稱為Old代:
這允許GC避免一次回收整個(gè)heap區(qū),取而代之遞增處理問題:每次只有collection set調(diào)用region的子集。每個(gè)階段期間所有的Young region被回收,但同樣的只包含一部分old region:
G1的另一個(gè)新特性是并發(fā)階段期間估算每一個(gè)region里包含存活數(shù)據(jù)的數(shù)量。這個(gè)被用于建立collection set:region包含的垃圾越多,越先被回收。因此名稱是:garbage-first 收集器。
為了激活JVM中G1收集器,按照下面的命令執(zhí)行你的應(yīng)用:
java -XX:+UseG1GC com.mypackages.MyExecutableClass
疏散(Evacuation)階段:Fully Young
在應(yīng)用程序生命周期的開始階段,在并發(fā)階段執(zhí)行之前,G1獲取不到任何附加信息,因此它的最初功能是full-yong模式。當(dāng)Young代塞滿了,應(yīng)用線程暫停,Young區(qū)的存活數(shù)據(jù)被復(fù)制到Survivor區(qū)域,任何空閑區(qū)域因此變成Survivor區(qū)。
復(fù)制對(duì)象過程被叫做疏散(Evacuation), 它的工作方式和我們之前看到其他Young收集器幾乎是一樣的。疏散階段full logs相當(dāng)大,因此在第一次full-young 疏散階段我們略去一些不相關(guān)的片段。并發(fā)階段之后我們會(huì)解釋大量細(xì)節(jié)。補(bǔ)充一點(diǎn),由于log記錄的全量尺寸,并行階段和“其他”階段的細(xì)節(jié)被抽取成獨(dú)立的片段:
0.134: [GC pause (G1 Evacuation Pause) (young), 0.0144119 secs]1 [Parallel Time: 13.9 ms, GC Workers: 8]2 …3 [Code Root Fixup: 0.0 ms]4 [Code Root Purge: 0.0 ms]5 [Clear CT: 0.1 ms] [Other: 0.4 ms]6 …7 [Eden: 24.0M(24.0M)->0.0B(13.0M) 8Survivors: 0.0B->3072.0K 9Heap: 24.0M(256.0M)->21.9M(256.0M)]10 [Times: user=0.04 sys=0.04, real=0.02 secs] 11
G1階段清理Young區(qū)域。JVM啟動(dòng)之后的134ms階段開始,通過鐘墻時(shí)間檢測(cè)階段持續(xù)了0.0144s。
表明下列活動(dòng)被8個(gè)并行GC線程實(shí)施耗費(fèi)13.9ms(real time)。
省略部分,細(xì)節(jié)在下面的系列片段。
釋放數(shù)據(jù)結(jié)構(gòu)用于管理并行活動(dòng)。通常應(yīng)該是靠近zero。這通常順序完成。
清除更多數(shù)據(jù)結(jié)構(gòu),通常應(yīng)該非??欤皇菐缀醯扔?。順序完成。
混雜其他活動(dòng),它們的很多是并行的。
細(xì)節(jié)可以看下面的章節(jié)。
階段前后的Eden區(qū)使用大小和容量大小。
階段前后被用于Survivor區(qū)的空間。
階段前后的heap區(qū)總使用大小和容量大小。
GC時(shí)間期間,不同類別的時(shí)長(zhǎng):
.user-回收期間GC線程消耗的總的cpu時(shí)間。 .sys-調(diào)用系統(tǒng)或等待系統(tǒng)事件的耗費(fèi)時(shí)長(zhǎng)。 .應(yīng)用程序的停頓的時(shí)鐘時(shí)間。GC期間并發(fā)活動(dòng)時(shí)長(zhǎng)理論上接近(user time+sys time)GC線程數(shù)量消費(fèi)的時(shí)長(zhǎng),這種情況下用了8個(gè)線程。注意的是由于一些活動(dòng)不是并行執(zhí)行,它會(huì)超過一定比率。
大多數(shù)重大事件被多個(gè)專用GC線程完成。它們的活動(dòng)在如下面片段的描述:
[Parallel Time: 13.9 ms, GC Workers: 8]1 [GC Worker Start (ms)2: Min: 134.0, Avg: 134.1, Max: 134.1, Diff: 0.1] [Ext Root Scanning (ms)3: Min: 0.1, Avg: 0.2, Max: 0.3, Diff: 0.2, Sum: 1.2] [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0] [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Code Root Scanning (ms)4: Min: 0.0, Avg: 0.0, Max: 0.2, Diff: 0.2, Sum: 0.2] [Object Copy (ms)5: Min: 10.8, Avg: 12.1, Max: 12.6, Diff: 1.9, Sum: 96.5] [Termination (ms)6: Min: 0.8, Avg: 1.5, Max: 2.8, Diff: 1.9, Sum: 12.2] [Termination Attempts7: Min: 173, Avg: 293.2, Max: 362, Diff: 189, Sum: 2346] [GC Worker Other (ms)8: Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] GC Worker Total (ms)9: Min: 13.7, Avg: 13.8, Max: 13.8, Diff: 0.1, Sum: 110.2] [GC Worker End (ms)10: Min: 147.8, Avg: 147.8, Max: 147.8, Diff: 0.0]
表明下列活動(dòng)被8個(gè)并行GC線程實(shí)施耗費(fèi)13.9ms(real time)。
線程開始活動(dòng)的合計(jì)時(shí)間,在階段的開始時(shí)間匹配時(shí)間戳。如果Min和Max差別很大,它也許表明太多線程被使用或者JVM里的GC進(jìn)程CPU時(shí)間被機(jī)器上其他進(jìn)程盜用。
掃描外部(非heap)Root消耗的時(shí)間例如clasloader,JNI引用,JVM系統(tǒng)等等。展示消耗時(shí)間,“Sum”是cpu時(shí)間。
掃描來自真實(shí)code Root的時(shí)長(zhǎng):局部變量等等。
從回收區(qū)域復(fù)制存活對(duì)象花費(fèi)的時(shí)間。
GC線程確定它們到達(dá)安全點(diǎn)消耗的時(shí)間,沒有多余工作完成,然后終止。
工作線程嘗試終止的次數(shù)。實(shí)際上線程發(fā)現(xiàn)有任務(wù)需要完成的時(shí)候嘗試失敗,過早去終止。
其他瑣碎的活動(dòng)不值得在日志里獨(dú)立片段展示。
任務(wù)線程總共花費(fèi)的時(shí)間。
任務(wù)線程完成工作的時(shí)間戳。通常它們因該大值相等,另一方面它也許顯示出太多線程無所事事,或者繁復(fù)的上下文工作。
此外,Evacuation階段期間一些混雜活動(dòng)被執(zhí)行。我們會(huì)講解它們的一部分在下面的片段。剩余部分隨后講解。
混雜其他的活動(dòng),大多數(shù)并行執(zhí)行。
處理非強(qiáng)引用的時(shí)間:清除或者確定不需要清理。
順序處理將剩下的非強(qiáng)引用從引用隊(duì)列中移除出去。
釋放收集集合里面區(qū)域花費(fèi)的時(shí)間以便它們適用于下一次分配。
并發(fā)標(biāo)記
從上面章節(jié)看出G1借鑒了CMS的許多理念,因此可以方便你充分理解之前的階段。雖然在一些方式上不盡相同,但是并發(fā)標(biāo)記的目標(biāo)非常類似。G1并發(fā)標(biāo)記使用STAB(Snapshot-At-The-Beginning)方法,意味著在周期的開始階段標(biāo)記所有存活對(duì)象,即使在收集期間已經(jīng)調(diào)整。存活對(duì)象被允許建立在每個(gè)區(qū)域(region)活躍性上,以便收集結(jié)合快速選擇。
這些信息隨后被用于執(zhí)行Old代GC。它可以完全并發(fā)執(zhí)行,一個(gè)僅僅包含垃圾的region被標(biāo)記,或者一個(gè)Old region “stop-the-world”evacuation階段包含垃圾和存活對(duì)象。
并發(fā)標(biāo)記開始于heap區(qū)已使用空間足夠大。默認(rèn)的,占45%,但是可以被JVM 選項(xiàng)InitiatingHeapOccupancyPercent 改變。類似CMS,G1并發(fā)標(biāo)記有一些小階段組成,它們中一些完全并發(fā),一些則不得不暫停應(yīng)用線程。
階段1:初始標(biāo)記。這個(gè)階段標(biāo)記所有從GC Root可達(dá)的對(duì)象。在CMS里,他需要“stop-the-world”,但是在G1, Evacuation階段它可以并行執(zhí)行,因此它的上限事最小的。你可以通過evacuation階段第一行添加“(initial-mark)”留意下GC日志里的這個(gè)階段:
1.631: [GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0062656 secs]
階段2:Root region掃描。這個(gè)階段標(biāo)記從所謂的root區(qū)域可達(dá)所有的存活對(duì)象,也就是標(biāo)記周期中間沒有必要分配的非空的對(duì)象。因?yàn)橐瞥龢?biāo)記周期中的填充會(huì)導(dǎo)致異常,這個(gè)階段必須在下一個(gè)階段開始之間完成。如果它提前開始,它會(huì)提前中止root掃描,然后等待完成。在目前的實(shí)現(xiàn)中,root區(qū)域在syrvivor區(qū)中:它們占據(jù)Young代小部分空間,在下一個(gè)Evacuation 階段被禁止回收。
1.362: [GC concurrent-root-region-scan-start] 1.364: [GC concurrent-root-region-scan-end, 0.0028513 secs]
階段3:并發(fā)標(biāo)記。這個(gè)階段和CMS的階段非常相似:它簡(jiǎn)單統(tǒng)計(jì)對(duì)象,在特別的bitmap中標(biāo)記可以訪問的對(duì)象。為了保證STAB語義論,為了達(dá)到標(biāo)記目的,通過可感知應(yīng)用線程放棄先前的引用G1 GC需要所有的并發(fā)線程更新到對(duì)象統(tǒng)計(jì)模式。
通過使用寫屏障(Pre-Write barriers,不要混淆于Post-Write barriers,以及涉及多線程的memory barriers,隨后進(jìn)行分析)。它們的職責(zé)是,每當(dāng)G1并發(fā)標(biāo)記期間你寫進(jìn)一個(gè)字段,在所謂的log buffer里面存儲(chǔ)之前結(jié)果,被并發(fā)標(biāo)記線程處理。
1.364: [GC concurrent-mark-start] 1.645: [GC concurrent-mark-end, 0.2803470 secs]
階段4:再標(biāo)記。這個(gè)階段需要“stop-the-world”,類似之前的CMS里面看到,在標(biāo)記階段完成。對(duì)于G1,對(duì)于遺留的部分它短暫的暫停應(yīng)用線程去阻塞并發(fā)更新日志和處理它們,標(biāo)記并發(fā)標(biāo)記開始的時(shí)候沒有標(biāo)記的存活對(duì)象。這個(gè)階段執(zhí)行一些額外的清理工作,例如,引用(查看Evacuation階段日志)處理,或者卸載的class。
1.645: [GC remark 1.645: [Finalize Marking, 0.0009461 secs] 1.646: [GC ref-proc, 0.0000417 secs] 1.646: [Unloading, 0.0011301 secs], 0.0074056 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
階段5:清除。最后的階段準(zhǔn)備即將到來的Evacuation階段,計(jì)算heap區(qū)所有的存活對(duì)象,通過預(yù)期的GC效率排序這些region。它通常執(zhí)行所有活動(dòng)的整理工作,為了下一次并發(fā)標(biāo)記迭代維持內(nèi)部狀態(tài)。
最后但同樣重要的是,包含不再使用的對(duì)象的region在這個(gè)階段被回收。這個(gè)階段的一些部分是并發(fā)執(zhí)行的,例如回收空region,大多數(shù)活躍性估算,但你在應(yīng)用線程不干涉的期間通常需要短暫的“stop-the-world”階段區(qū)確定方案。日志“stop-the-world”階段和下圖類似:
1.652: [GC cleanup 1213M->1213M(1885M), 0.0030492 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
一旦發(fā)現(xiàn)只包含垃圾對(duì)象的region,日志格式會(huì)有些區(qū)別,類似于:
1.872: [GC cleanup 1357M->173M(1996M), 0.0015664 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 1.874: [GC concurrent-cleanup-start] 1.876: [GC concurrent-cleanup-end, 0.0014846 secs]
疏散階段:混合
并發(fā)清除可以空出來整個(gè)old代是令人興奮,但是事實(shí)情況是不是每次都這樣。并發(fā)標(biāo)記已經(jīng)成功完成之后,G1會(huì)安排一次混合回收,不僅僅是回收young region的垃圾,還把old region的一部分放進(jìn)collection set中。
一次混合疏散階段不是經(jīng)常緊隨并發(fā)標(biāo)記階段結(jié)束之后。有一系列的規(guī)則和試探影響這個(gè)階段。例如,并發(fā)空出來old region中的大塊區(qū)域是可能的,然而根本沒有必要去做。
它們可能因此在并發(fā)標(biāo)記結(jié)束和混合evacuation階段之間簡(jiǎn)單發(fā)生一系列full-young evacuation階段。
old區(qū)準(zhǔn)確的對(duì)象被添加進(jìn)collection set,保證其添加順序,根據(jù)一些規(guī)則挑選出順序。這些包含了為了達(dá)到應(yīng)用程序的軟實(shí)時(shí)性能目標(biāo)。并發(fā)標(biāo)記期間,活躍的和GC 效率的數(shù)據(jù)被回收,還有一些JVM配置選項(xiàng)?;旌匣厥者M(jìn)程和初期分析的full-young gc一樣龐大,但是這次我們會(huì)包含remembered set的子集。
Remembered set運(yùn)行heap不同region使用獨(dú)立的收集器。例如,當(dāng)回收區(qū)域A,B和C,我們必須知道D和E中任何一個(gè)引用它們,去確定它們的活躍性。但是統(tǒng)計(jì)整個(gè)heap區(qū)花費(fèi)很長(zhǎng)時(shí)間,銷毀整個(gè)增量收集,因此最佳化被破壞。很像為了使用其他GC算法獨(dú)立手機(jī)Young region我們使用卡表( Card Table),在G1中我們使用Remembered Sets。
正如上面插圖展示那樣,每一個(gè)region有一個(gè)remembered sets列出外部引用指向這個(gè)區(qū)域。這些被看作額外的GC Roots。注意的是并發(fā)標(biāo)記期間old區(qū)域被判斷為垃圾的對(duì)象,即使外部引用它們會(huì)被忽略:那種情況下被當(dāng)作垃圾的參照?qǐng)D:
下一步操作和其他收集器一樣:多個(gè)并行GC線程計(jì)算出哪些對(duì)象存活,哪些是垃圾:
最后,存活對(duì)象被移到Survior區(qū)域中,如果有必要?jiǎng)t新建。空的orgion被釋放出來,用戶再次存儲(chǔ)對(duì)象:
為了維護(hù)Remembered Sets,應(yīng)用運(yùn)行期間,每當(dāng)寫入操作執(zhí)行的時(shí)候觸發(fā)一個(gè)Post-Write Barrier。如果一個(gè)引用跨越region,也就是一個(gè)region指向另一個(gè)region,目標(biāo)region的 Remembered Set存入相對(duì)應(yīng)的entry中。為了減少write barrier,將card放進(jìn)Remember Set過程異步執(zhí)行,突出性能最優(yōu)化。但是基本上將dirty card信息放進(jìn)本地緩存的方式存入Write barrier,一個(gè)專門GC線程將信息引用region的Remember set中。
混合模式中,對(duì)照fully young模式log展示一些有趣的方面:
[Update RS (ms)1: Min: 0.7, Avg: 0.8, Max: 0.9, Diff: 0.2, Sum: 6.1] [Processed Buffers2: Min: 0, Avg: 2.2, Max: 5, Diff: 5, Sum: 18] [Scan RS (ms)3: Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8] [Clear CT: 0.2 ms]4 [Redirty Cards: 0.1 ms]5
自并發(fā)執(zhí)行Remember Set以后,我必須確保真正收集開始之前still-buffered cards被執(zhí)行。如果數(shù)量很多,并發(fā)GC線程無法負(fù)載。他可能是,舉例來說,勢(shì)不可擋的到來的字段修改
的數(shù)量,或者CPU資源不足。
每一個(gè)任務(wù)線程操作local buffer的數(shù)量。
從Remember Set中掃描引用的數(shù)量。
清理card table中的card花費(fèi)的時(shí)間。Remember set通過簡(jiǎn)單移除“dirty”(表示filed被修改)狀態(tài)進(jìn)行清理工作。
card table超著ditry card的占用位置花費(fèi)的時(shí)間。GC自己操作通過heap中發(fā)生突變被定義為位置占用,例如引用隊(duì)列。
總結(jié)
這個(gè)應(yīng)該建立在充分理解G1如果工作基礎(chǔ)之上,這些當(dāng)然為了簡(jiǎn)介,需要我們忽略相當(dāng)多的一些實(shí)現(xiàn)細(xì)節(jié),像humongous objects的細(xì)節(jié)。綜合考慮,G1是HotSpot中現(xiàn)有的最先進(jìn)的收集器產(chǎn)品,在G1上,他被HotSpot的工程師無所不用其極地改進(jìn),在即將到來的Java 新版本。
正如他我所看到的,G1修正了CMS的廣為人知的問題,從階段可預(yù)測(cè)到heap碎片。使得應(yīng)用不再受限于CPU利用率,但是對(duì)個(gè)別選項(xiàng)十分敏感。G1很可能是對(duì)HotSpot用戶來說最好的選擇,尤其是運(yùn)行最新版本的Java。然而,這性能升不是毫無代價(jià):G1吞吐量歸功于附加的write barrier和更多后臺(tái)活動(dòng)線程。如果應(yīng)用是吞吐量?jī)?yōu)先或者CPU使用率100%,不關(guān)注個(gè)別階段,CMS,甚至Parallel或許是更好的選擇。
唯一可行選擇合適的GC算法和設(shè)置的方式通過嘗試和錯(cuò)誤,但是我還在下一章節(jié)給出一般的參考。
注意的是G1很有可能是java 9默認(rèn)的GC收集器:http://openjdk.java.net/jeps/248
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/66341.html
摘要:注意到通過并發(fā)更新它們來幫助維護(hù)通常在應(yīng)用運(yùn)行期間。累積狀態(tài)包括的總和和最大數(shù)量,已占用的數(shù)量,最大尺寸信息。階段期間和標(biāo)記期間并發(fā)標(biāo)記多階段的一部分處理引用。之后,期間的清除階段死亡的被回收。 原文出處:Tips for Tuning the Garbage First Garbage Collector 這是由兩部分組成的系列的第二篇關(guān)于G1垃圾回收器的文章,你可以在2013.07...
摘要:垃圾收集器簡(jiǎn)述全文共兩部分有基礎(chǔ)的讀者只需要閱讀第一部分垃圾收集器在最新幾個(gè)版本的發(fā)展第二部分為基礎(chǔ)部分垃圾收集器在最新幾個(gè)版本的發(fā)展垃圾收集器始見于版本在后續(xù)的幾個(gè)版本中對(duì)它進(jìn)行了優(yōu)化和改進(jìn)在中垃圾收集器增加了幾個(gè)可配置選項(xiàng)的自動(dòng)發(fā)現(xiàn)功能 G1垃圾收集器簡(jiǎn)述 全文共兩部分,有基礎(chǔ)的讀者只需要閱讀第一部分G1垃圾收集器在最新幾個(gè)版本的發(fā)展,第二部分為基礎(chǔ)部分. G1垃圾收集器在最新幾個(gè)...
摘要:之前根據(jù)的內(nèi)存管理白皮書介紹了在分代算法中的幾個(gè)垃圾收集器,本文將介紹垃圾收集器。本節(jié)介紹的收集過程,收集器主要包括了以下種操作年輕代收集并發(fā)收集,和應(yīng)用線程同時(shí)執(zhí)行混合式垃圾收集必要時(shí)的接下來,我們進(jìn)行一一介紹。 之前根據(jù) Sun 的內(nèi)存管理白皮書介紹了在 HotSpot JVM 分代算法中的幾個(gè)垃圾收集器,本文將介紹 G1 垃圾收集器。 G1 的主要關(guān)注點(diǎn)在于達(dá)到可控的停頓時(shí)間,在...
摘要:適用收集場(chǎng)景新生代收集老年代收集并行收集器又叫吞吐量收集器應(yīng)用于多核系統(tǒng)。它是為了平衡延時(shí)和吞吐量之間的一種最優(yōu)關(guān)系。 回顧傳統(tǒng)垃圾回收器 HotSpot 垃圾收集器實(shí)現(xiàn) Serial Collector(串型收集器) 使用場(chǎng)景,大多數(shù)服務(wù)器是單核CPU。適用收集場(chǎng)景:1. 新生代收集(Young Generation Collection)2. 老年代收集(Old Genera...
摘要:可預(yù)測(cè)的停頓這是相對(duì)于的另一個(gè)大優(yōu)勢(shì),降低停頓時(shí)間是和共同的關(guān)注點(diǎn),但除了追求低停頓外,還能建立可預(yù)測(cè)的停頓時(shí)間模型,能讓使用者明確指定在一個(gè)長(zhǎng)度為毫秒的時(shí)間片段內(nèi)。 Stop The World:不管選擇哪種GC算法,stop-the-world都是不可避免的。Stop-the-world意味著從應(yīng)用中停下來并進(jìn)入到GC...
閱讀 3748·2021-09-22 15:28
閱讀 1362·2021-09-03 10:35
閱讀 946·2021-09-02 15:21
閱讀 3551·2019-08-30 15:53
閱讀 3550·2019-08-29 17:25
閱讀 631·2019-08-29 13:22
閱讀 1613·2019-08-28 18:15
閱讀 2354·2019-08-26 13:57