总结:G1收集器
一、介紹
G1 GC?全稱是Garbage First Garbage Collector,垃圾優(yōu)先垃圾回收器,簡稱G1 。
G1 收集器是一個面向服務器模式的垃圾收集器,主要為多CPU機器和大內(nèi)存機器設(shè)計。它可以盡可能在達到高吞吐量同時滿足垃圾收集暫停時間目標。G1 有以下幾個優(yōu)點:
1. 能夠像CMS收集器一樣跟應用線程并行操作
2.復制對象的同時對對象進行壓縮和釋放內(nèi)存
3.靈活分配老年代,年輕代占比,不再固定。?盡管G1堆內(nèi)存仍然是分代的,但是同一個代的內(nèi)存不再采用連續(xù)的內(nèi)存結(jié)構(gòu)。
4. 收縮空閑空間不會造成由長GC引起的應用停頓時間。
5. 更精確的預測GC停頓時間
6.理論上內(nèi)存空間越大越好,因為老年代不是快滿了才進行一次GC,而是平時也會進行多次混合GC
二、GC收集器比較
傳統(tǒng)的垃圾收集器像Serial, Parallel, CMS 全部都將Java堆分成三個固定內(nèi)存大小的部分,如年輕代,年老代和永久代,所有的內(nèi)存中的Java對象都在三個部分中結(jié)束。
G1 收集器采用了不同的方法,它將整個Java堆分成2048個相同大小的堆區(qū)域( 內(nèi)存分段是物理概念,代表實際的物理內(nèi)存空間),可以用-XX:G1HeapRegionSize調(diào)整內(nèi)存分段的個數(shù)。這些區(qū)域集像以前的收集器一樣被分配到不同的角色,每個內(nèi)存分段都可以被標記為Eden區(qū),Survivor區(qū),Old區(qū),或者Humongous區(qū)。但是沒有固定的區(qū)域數(shù)量被明確固定在某個角色上(所以不要配置年輕代大小),不同區(qū)的內(nèi)存分段就不必是連續(xù)內(nèi)存空間,這將為內(nèi)存使用上提供了更大的靈活性。
關(guān)于對象的內(nèi)存分配,方式如下:
新分配的對象會被分配到Eden區(qū)的內(nèi)存分段上,每一次年輕代的回收過程都會把Eden區(qū)存活的對象復制到Survivor區(qū)的內(nèi)存分段上,把Survivor區(qū)繼續(xù)存活的對象年齡加1,如果Survivor區(qū)的存活對象年齡達到某個閾值(比如15,可以設(shè)置),Survivor區(qū)的對象會被復制到Old區(qū)。復制過程是把源內(nèi)存分段中所有存活的對象復制到空的目標內(nèi)存分段上,復制完成后,源內(nèi)存分段沒有了存活對象,變成了可以使用的空的Eden內(nèi)存分段了;而目標內(nèi)存分段的對象都是連續(xù)存儲的,沒有碎片,所以復制過程可以達到內(nèi)存整理的效果,減少碎片。Humongous區(qū)用于保存大對象,如果一個對象占用的空間超過內(nèi)存分段的一半(比如上面的8M),則此對象將會被分配在Humongous區(qū)。如果對象的大小超過一個甚至幾個分段的大小,則對象會分配在物理連續(xù)的多個Humongous分段上。Humongous對象因為占用內(nèi)存較大并且連續(xù)會被優(yōu)先回收。
在執(zhí)行垃圾收集時,G1操作十分類似于CMS收集器。G1執(zhí)行一個并發(fā)全局標記階段貫穿整個Java堆去判斷對象的活躍度,標記階段結(jié)束后,收集器知道哪一些區(qū)域是空的或者接近空的,它將先收集這些空的區(qū)域,通常會釋放大量的內(nèi)存空間,G1將其收集和壓縮活動集中在可能充滿可回收對象的堆區(qū)域上, 這也是收集器命名為G1的原因(即優(yōu)先收集垃圾占比比較高的堆區(qū)域)。G1使用了一個停頓預測模型根去滿足用戶自定義的停頓時間目標并且根據(jù)該目標選擇一定數(shù)量的區(qū)域進行回收。
G1通過轉(zhuǎn)移的方法將已被G1標識為區(qū)域當作可回收的垃圾進行回收, 它把對象從1個或者多個區(qū)域中復制到單個區(qū)域,在復制的同時進行 壓縮對象和釋放內(nèi)存(這里的壓縮不是對對象的壓縮,而是通過對對象內(nèi)存碎片進行整理而騰出連續(xù)空間的意思,如多個堆區(qū)域的數(shù)據(jù)復制到一個或較少的堆區(qū)域中),所有的轉(zhuǎn)移過程通過多個處理器并行處理(可以考慮增加線程數(shù))以降低暫停時間和增加吞吐量。因此,每一次垃圾回收,G1收集器持續(xù)地在用戶預定的暫停時間內(nèi)減少內(nèi)存碎片。相對而言,CMS收集器不會做壓縮,而PARALlelOld 收集器是對整個Java堆執(zhí)行壓縮,那么將導致較長停頓時間。
如果應用從CMS收集器或者ParallelOldGC到G1,你可能會發(fā)現(xiàn)相同數(shù)據(jù)規(guī)模情況下G1占用的內(nèi)存比以前多了一些,多出的一些內(nèi)存主要是用于存儲賬單數(shù)據(jù)結(jié)果,如Remembered Sets和Collection Sets.
- Remembered Sets, RSets跟蹤每一個區(qū)域的對象引用,每一個區(qū)域都有一個Rsets, 它在區(qū)域啟用一個獨立且并行的集合,RSets大約增加5%左右的Overhead。
- Collection Sets, CSets是放置可被收集器回收的區(qū)域集合,所有在這個集合的數(shù)據(jù)對象在垃圾回收期間均可被轉(zhuǎn)移,這些區(qū)域可能是Eden,Survivor 或者老年代。CSets大約有增加1%左右的overhead.
和CMS對比:
| G1 | CMS | |
| 設(shè)計原則 | 首先收集盡可能多的垃圾(Garbage First) | 盡可能少而塊地執(zhí)行GC,以停頓時間為目標 |
| 垃圾回收時機 | ?啟發(fā)式算法,在老年代找出具有高收集收益的分區(qū)進行收集 | ?內(nèi)存耗盡(新生代)或者快耗盡(老年代) |
| ?內(nèi)存劃分 | 將內(nèi)存劃分為一個個相等大小的內(nèi)存分區(qū)(Region),每個區(qū)域都可能有四種狀態(tài):E(eden)、S(Survial)、O(old)、空閑 | ?分為新生代和老年代2塊連續(xù)的內(nèi)存空間 |
| ?是否產(chǎn)生垃圾碎片 | 將一組或多組區(qū)域(稱為回收集 (CSet))中的存活對象以增量、并行的方式復制到不同的新區(qū)域來實現(xiàn)壓縮,從而減少堆碎片 | 采用標記刪除,會產(chǎn)生碎片? |
三、G1 回收步驟
四、G1 調(diào)優(yōu)注意事項
1. 年輕代大小
避免使用-Xmn選項或-XX:NewRatio等其他相關(guān)選項顯式設(shè)置年輕代大小,因為固定年輕代的大小會覆蓋暫停時間目標。
G1會動態(tài)調(diào)節(jié)年輕代和總堆的比例。?
G1可以根據(jù)用戶設(shè)置的暫停時間目標自動調(diào)整年輕代和總堆大小, 暫停目標越短年輕代空間越小(年輕代小,需要回收的空間可能就會小,但是回收次數(shù)也就會更多);
整個年輕代內(nèi)存會在初始空間-XX:G1NewSizePercent(默認整堆5%)與最大空間-XX:G1MaxNewSizePercent(默認60%)之間動態(tài)變化,
且由參數(shù)目標暫停時間-XX:MaxGCPauseMillis(默認200ms)、需要擴縮容的大小以及分區(qū)的已記憶集合(RSet)計算得到。
當然,G1依然可以設(shè)置固定的年輕代大小(參數(shù)-XX:NewRatio、-Xmn),但同時暫停目標將失去意義。
2. 暫停時間目標
每當對垃圾回收進行評估或調(diào)優(yōu)時,都會涉及到延遲與吞吐量的權(quán)衡。G1是增量垃圾回收器, 其吞吐量目標是 90% 的應用程序時間和 10%的垃圾回收時間。因此,暫停時間目標不要太嚴苛。目標太過嚴苛表示您愿意承受更多的垃圾回收開銷,而這會直接影響到吞吐量(很明顯的是Alarm組件,為了滿足P99和P95的延時目標,設(shè)置了 -XX:MaxGCPauseMillis=300 ,導致總的GC時間提升了將近一倍,這顯然會降低Alarm組件的吞吐量,因為CPU時間是固定的,GC時間多就會影響應用程序的CPU時間)。
G1有一個及其重要的特性:軟實時(soft real-time)。所謂的實時垃圾回收,是指在要求的時間內(nèi)完成垃圾回收。“軟實時”則是指,用戶可以指定垃圾回收時間的限時,G1會努力在這個時限內(nèi)完成垃圾回收,但是G1并不擔保每次都能在這個時限內(nèi)完成垃圾回收。通過設(shè)定一個合理的目標,可以讓達到90%以上的垃圾回收時間都在這個時限內(nèi)。
五、G1 主要參數(shù)配置
六、垃圾回收過程
G1垃圾回收過程主要包括三個:
- 年輕代回收(young gc)過程
- 老年代并發(fā)標記(concurrent marking)過程
- 混合回收過程(mixed gc)。
1、YGC
在Eden充滿時觸發(fā),在回收之后所有之前屬于Eden的區(qū)塊全變成空白。然后把剩余的存活對象移動到S區(qū)。
很多情況下,S區(qū)的對象會有部分晉升到Old區(qū),另外如果S區(qū)已滿、Eden存活的對象會直接晉升到Old區(qū),這種情況下Old的空間就會漲(詳見下圖,Old區(qū)在Eden區(qū)回收后,使用率漲了一點,S區(qū)滿,導致存活對象無法放到S區(qū),從而直接放入了老年代)
這個過程會對年輕代的對象有一個短暫的停頓
年輕代回收過程如下【回收時機:當所有的Eden區(qū)都滿了】:
JVM啟動時,G1先準備好Eden區(qū),程序在運行過程中不斷創(chuàng)建對象到Eden區(qū),當所有的Eden區(qū)都滿了,G1會啟動一次年輕代垃圾回收過程。年輕代只會回收Eden區(qū)和Survivor區(qū)。首先G1停止應用程序的執(zhí)行(Stop-The-World),G1創(chuàng)建回收集(Collection Set),回收集是指需要被回收的內(nèi)存分段的集合,年輕代回收過程的回收集包含年輕代Eden區(qū)和Survivor區(qū)所有的內(nèi)存分段。然后開始如下回收過程:
第一階段,掃描根。
根是指static變量指向的對象,正在執(zhí)行的方法調(diào)用鏈條上的局部變量等。根引用連同RS記錄的外部引用作為掃描存活對象的入口。
第二階段,更新RS。
處理dirty card queue中的card,更新RS。此階段完成后,RS可以準確的反映老年代對所在的內(nèi)存分段中對象的引用。
第三階段,處理RS。
識別被老年代對象指向的Eden中的對象,這些被指向的Eden中的對象被認為是存活的對象。
第四階段,復制對象。
此階段,對象樹被遍歷,Eden區(qū)內(nèi)存段中存活的對象會被復制到Survivor區(qū)中空的內(nèi)存分段,Survivor區(qū)內(nèi)存段中存活的對象如果年齡未達閾值,年齡會加1,達到閥值會被會被復制到Old區(qū)中空的內(nèi)存分段。
第五階段,處理引用。
處理Soft,Weak,Phantom,Final,JNI Weak?等引用。
2、 G1老年代并發(fā)標記過程【發(fā)生時機:整個堆內(nèi)存被占滿一定大小的時候,默認是45%】
注意:這個階段會回收百分之百為垃圾的內(nèi)存分段,并不只是做標記。
當整個堆內(nèi)存(包括老年代和新生代)被占滿一定大小的時候(默認是45%,可以通過-XX:InitiatingHeapOccupancyPercent進行設(shè)置),老年代回收過程會被啟動。具體檢測堆內(nèi)存使用情況的時機是年輕代回收之后(即經(jīng)過一次YGC之后,整個堆仍然使用了超過45%,則處罰OGC)或者houmongous對象分配之后。老年代回收包含標記老年代內(nèi)的對象是否存活的過程,標記過程是和應用程序并發(fā)運行的(不需要Stop-The-World)。應用程序會改變指針的指向,并發(fā)執(zhí)行的標記過程怎么能保證標記過程沒有問題呢?并發(fā)標記過程有一種情形會對存活的對象標記不到。假設(shè)有對象A,B和C,一開始的時候B.c=C,A.c=null。當A的對象樹先被掃描標記,接下來開始掃描B對象樹,此時標記線程被應用程序線程搶占后停下來,應用程序把A.c=C,B.c=null。當標記線程恢復執(zhí)行的時候C對象已經(jīng)標記不到了,這時候C對象實際是存活的,這種情形被稱作對象丟失。G1解決的方法是在對象引用被設(shè)置為空的語句(比如B.c=null)時,把原先指向的對象(C對象)保存到一個隊列,代表它可能是存活的。然后會有一個重新標記(Remark)過程處理這些對象,重新標記過程是Stop-The-World的,所以可以保證標記的正確性。上述這種標記方法被稱為開始時快照技術(shù)(SATB,Snapshot At The Begging)。這種方式會造成某些是垃圾的對象也被當做是存活的,所以G1會使得占用的內(nèi)存被實際需要的內(nèi)存大。
具體標記過程如下:
1. 先進行一次年輕代回收過程,這個過程是Stop-The-World的,同時會進行一次初始標記。
??老年代的回收基于年輕代的回收(比如需要年輕代回收過程對于根對象的收集,初始的存活對象的標記)。
2. 恢復應用程序線程的執(zhí)行。
3. 開始老年代對象的標記過程。
此過程是與應用程序線程并發(fā)執(zhí)行的。標記過程會記錄弱引用情況,還會計算出每個分段的對象存活數(shù)據(jù)(比如分段內(nèi)存活對象所占的百分比)。
4. Stop-The-World。
5. 重新標記(Remark)。
此階段重新標記前面提到的STAB隊列中的對象(例子中的C對象),還會處理弱引用。
????????? ? 注意:如果標記過程沒有被其它線程打斷的話,就不需要重新標記了。
6. 回收百分之百為垃圾的內(nèi)存分段。
注意:不是百分之百為垃圾的內(nèi)存分段并不會被處理,這些內(nèi)存分段中的垃圾是在混合回收過程(Mixed GC)中被回收的。
由于Humongous對象會獨占整個內(nèi)存分段,如果Humongous對象變?yōu)槔?#xff0c;則內(nèi)存分段百分百為垃圾,所以會在第一時間被回收掉。
7. 恢復應用程序線程的執(zhí)行。
G1并發(fā)標記周期可以分成幾個階段、其中有些需要暫停應用線程(所以G1也會STW)。有些是后臺并行處理,不需要暫停應用
3、混合GC(也會SWT)
并發(fā)標記過程結(jié)束以后,緊跟著就會開始一系列的混合GC。混合回收的意思是年輕代和老年代會同時被回收。這個時期因為會同時進行YGC和清理上面已標記為X的區(qū)域(意思可以理解為同時進行YGC和Old GC),所以稱之為混合階段。 混合回收的算法和年輕代回收的算法完全一樣,只是回收集多了老年代的內(nèi)存分段。具體過程請參考上面的年輕代回收過程。?
每次混合GC只是清理一部分的O區(qū)內(nèi)存,整個GC會一直持續(xù)到幾乎所有的標記區(qū)域垃圾對象都被回收(即多次混合GC),這個階段完了之后G1會重新回到正常的YGC階段。周期性的,當O區(qū)內(nèi)存占用達到一定數(shù)量之后G1又會開啟一次新的并行GC階段.
由于老年代中的內(nèi)存分段默認分8次回收,G1會優(yōu)先回收垃圾多的內(nèi)存分段。垃圾占內(nèi)存分段比例越高的,越會被先回收。并且有一個閾值會決定內(nèi)存分段是否被回收,-XX:G1MixedGCLiveThresholdPercent,默認為65%,意思是垃圾占內(nèi)存分段比例要達到65%才會被回收。如果垃圾占比太低,意味著存活的對象占比高,在復制的時候會花費更多的時間。?
混合回收并不一定要進行8次。有一個閾值-XX:G1HeapWastePercent,默認值為10%,意思是允許整個堆內(nèi)存中有10%的空間被浪費,意味著如果發(fā)現(xiàn)可以回收的垃圾占堆內(nèi)存的比例低于10%,則不再進行混合回收。因為GC會花費很多的時間但是回收到的內(nèi)存卻很少。
4、FullGC
如果對象內(nèi)存分配速度過快,mixed gc來不及回收,導致老年代被填滿,就會觸發(fā) 一次full gc,G1的full gc算法就是單線程執(zhí)行的serial old gc,會導致異常長時間的暫停時間,需要進行不斷的調(diào)優(yōu),盡可能的避免full gc.
七、G1相較于其它垃圾收集器的優(yōu)勢
1、啟發(fā)式算法,在老年代找出具有高收集收益的分區(qū)進行收集
這樣的好處是什么?分析下,假設(shè)有A,B,C,D,E五個堆區(qū)域,A的垃圾對象占比為90%,對象垃圾占比805,CDE三個對象占比都在10% ~ 40%之間,很顯然,回收AB兩個區(qū)域能騰出更大的空間,且回收速度更快,因此受益是最好的,所以對于G1來說,某一次垃圾回收可以只回收AB兩個區(qū)域,假設(shè)此次回收耗時1秒;下次進行GC的時候,CDE的垃圾占比達到了70%,那么回收CDE的收益又好了,所以進行回收CDE,耗時1秒;
經(jīng)過以上分析,我們發(fā)現(xiàn)兩次GC時間總共是:1 + 1 = 2秒,GC次數(shù)是2次;
如果換成CMS或其它垃圾回收器會怎么樣呢?
仍然是ABCDE五個區(qū)間,此時由于是老年代,所以只有O區(qū)快慢了才會進行一次全量的回收,那么就同時需要回收ABCDE五個區(qū)域,AB回收時間和G1是一樣的,但是CDE就明顯要打了,因為CDE的垃圾占比低,也就是說需要復制更多的對象,假設(shè)我們算這個時間是2秒,GC次數(shù)是1次。
經(jīng)過以上分析,CMS的GC總時間是1 + 2 = 3秒。GC次數(shù)是1次。
總結(jié):G1的按照收益的方式去收集比CMS的GC總耗時更少,但是收集次數(shù)會更多。
2、年輕代和老年代空間不再固定
這樣比較靈活,系統(tǒng)會根據(jù)各個代的大小動態(tài)的調(diào)節(jié),這樣可以控制GC的時間
3、充分利用系統(tǒng)大內(nèi)存
目前服務器內(nèi)存一般比較高,而G1非常適合大內(nèi)存服務器,避免其它收集器fullgc時間過長的問題,其它收集器的fullgc幾乎是不可避免的,放內(nèi)存很大的時候,fullgc會非常耗時
參考:
G1垃圾收集器入門
Java G1 GC 垃圾回收深入淺出
深入理解Java G1垃圾收集器
老大難的GC原理及調(diào)優(yōu),這下全說清楚了
總結(jié)
- 上一篇: cocos2dx3.2开发 RPG《Fl
- 下一篇: 基础会计学习笔记4 会计核算基本方法(会