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