您好,登錄后才能下訂單哦!
這篇文章給大家介紹java中的G1回收器怎么用,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
G1回收器是在jdk1.7中正式使用的全新垃圾回收器,并且是jdk9及之后版本的默認回收器。從分代上看,G1 依然屬于分代垃圾回收器,它會區分年輕代和老年代,依然有eden區和survivor區,但從堆的結構上看,它并不要求整個eden區、年輕代或者老年代都連續。G1使用了全新的分區算法,特點如下:
并行性:G1在回收期間,可以由多個GC線程同時工作,有效利用多核計算能力。
并發性:G1在擁有與應用程序交替執行的能力,部分工作可以和應用程序同時執行,一般來說,不會在整個回收期間完全阻塞應用程序。
分代GC:G1依然是一個分代回收器,但是和之前的回收器不同,它同時兼顧年輕代和老年代,其他回收器或者工作在年輕代,或者工作在老年代。
空間整理:G1在回收過程中,會進行適當的對象移動,不像CMS,只是簡單地標記清理對象,在若干次GC后,CMS必須進行一次碎片整理。而G1不同,它每次回收都會有效地復制對象,減少碎片空間。
可預見性:由于分區的原因,G1可以只選取部分區域進行內存回收,這樣縮小了回收的范圍,全局停頓也能得到較好的控制。
G1將堆進行分區,劃分為一個個的區域,每次回收時,只回收其中幾個區域,以此來控制垃圾回收產生的一次停頓時間。
G1的回收過程可能有4個階段:
新生代GC
并發標記周期
混合回收
如果需要,可能會進行FullGC
新生代GC的主要工作是回收eden區和survivor區。一旦eden區被占滿,新生代GC就會啟動。新生代GC只處理eden區和survivor區,回收后所有的eden區都應該被清空,而survivor區會被回收一部分數據。
G1 的并發階段和 CMS 有點類似,它們都是為了降低一次停頓時間,而將可以和應用程序并發的部分單獨提取出來執行。
并發標記周期可以分為以下幾步:
初始標記:標記從根節點直接可達的對象。這個階段會伴隨一次新生代GC,它是產生全局停頓的,應用程序線程在這個階段必須停止執行。
根區域掃描:由于初始標記必然會伴隨一次新生代GC,所以在初始化標記后,eden區被清空,并且存活對象被移入survivor區。在這個階段,將掃描由survivor區直接可達的老年代區域,并標記這些直接可達的對象。這個過程是可以和應用程序并發執行的,但是根區域掃描不能和新生代GC同時執行,因為如果恰巧在此時需要進行新生代GC,就需要等待根區域掃描結束后才能進行。如果發生這種情況,這次新生代GC的時間就會延長。
并發標記:和CMS類似,并發標記將會掃描并查找整個堆的存活對象,并做好標記。這是一個并發的過程,并且這個過程可以被一次新生代GC打斷。
重新標記:和CMS一樣,重新標記也是會產生應用程序停頓的。由于并發標記過程中,應用程序依然在運行,因此標記結果可能需要修正,所以在此對上一次的標記結果進行補充。在 G1 中,這個過程使用SATB(Snapshot-At-The-Beginning
)算法完成,即G1會在標記之初為存活對象創建一個快照,這個快照有助于加速重新標記的速度。
獨占清理:這個階段是會引起停頓的。它將計算各個區域的存活對象和GC回收比例,并進行排序,識別可供混合回收的區域。識別可供混合回收的區域。在這個階段,還會更新記憶集(Remebered Set
)。該階段給出了需要被混合回收的區域并進行了標記,在混合回收階段需要這些信息。
并發清理:這里會識別并清理完全空閑的區域。它是并發的清理,不會引起停頓。
除了初始標記、重新標記和獨占清理,其他幾個階段都可以和應用程序并發執行。
在并發標記周期中,G1會產生如下日志:
[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0014636 secs] ... [Eden: 2048.0K(14.0M)->0.0B(13.0M) Survivors: 0.0B->1024.0K Heap: 13.8M(40.0M)->2624.1K(40.0M)] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC concurrent-root-region-scan-start] [GC concurrent-root-region-scan-end, 0.0003832 secs]
[GC concurrent-mark-start] [GC pause (young), 0.0003382 secs] ... [Eden: 2048.0K(14.0M)->0.0B(13.0M) Survivors: 0.0B->1024.0K Heap: 13.8M(40.0M)->2624.1K(40.0M)] [Times: user=0.01 sys=0.00, real=0.00 secs] ... [Eden: 2048.0K(14.0M)->0.0B(13.0M) Survivors: 0.0B->1024.0K Heap: 13.8M(40.0M)->2624.1K(40.0M)] [Times: user=0.01 sys=0.00, real=0.00 secs] ... [Eden: 2048.0K(14.0M)->0.0B(13.0M) Survivors: 0.0B->1024.0K Heap: 13.8M(40.0M)->2624.1K(40.0M)] [Times: user=0.01 sys=0.00, real=0.00 secs] [GC concurrent-mark-end, 0.0003929 secs]
[GC remark [Finalize Marking, 0.0006027 secs] [GC ref-proc, 0.0000295 secs] [Unloading, 0.0003390 secs], 0.0010737 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC cleanup 10M->10M(40M), 0.0003016 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC concurrent-cleanup-start] [GC concurrent-cleanup-end, 0.0000016 secs]
在并發標記周期中,雖然有部分對象被回收,但是總體上說,回收的比例是相當低的。但是在并發標記周期后,G1已經明確知道哪些區域含有比較多的垃圾對象,在混合回收階段就可以專門針對這些區域進行回收。這個階段叫做混合回收,是因為這個階段既會執行正常的年輕代GC,又會選取一些被標記的老年代區域進行回收,它同時處理了新生代和老年代。
混合GC會產生如下日志:
[GC pause (mixed), 0.0003382 secs] ... [Eden: 2048.0K(14.0M)->0.0B(13.0M) Survivors: 0.0B->1024.0K Heap: 13.8M(40.0M)->2624.1K(40.0M)] [Times: user=0.01 sys=0.00, real=0.00 secs]
混合GC會執行多次,走到回收了足夠多的內存空間,然后它會觸發一次新生代GC。新生代GC后,又可難會發生一次并發標記周期的處理,最后又會引起混合GC的執行。
和CMS類似,并發回收由于讓應用程序和GC線程交替工作,總是不能完全避免在特別繁忙的場合出現在回收過程中內存不充足的情況。當遇到這種情況時,G1也會轉入一個FullGC。
當G1在并發標記時,由于老年代被快速填充,G1會終止并發標記而轉入一個Full GC:
[GC concurrent-mark-start] [Full GC 898->896(900M), 0.7003382 secs] [Eden: 0K(45.0M)->0.0B(45.0M) Survivors: 0.0B->0B Heap: 898.7M(900.0M)->896.2M(900.0M)] [Times: user=1.01 sys=0.00, real=0.075 secs] [GC concurrent-mark-abort]
此外,如果在混合GC時空間不足,或者在新生代GC時survivor區和老年代無法容納幸存對象,都會導致一次FullGC.
# 表示應用程序發生了一次新生代GC,這是在初始標記時發生的,耗時0.0018193秒,意味著程序至少暫停了0.0018193秒 [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0018193 secs] # 后續并行時間,表示所有GC線程總的花費時間,這里為0.9毫秒,workers為8表示有8個GC線程 [Parallel Time: 0.9 ms, GC Workers: 8] # GC線程的執行情況,這里統計了8個線程的統計值,如 平均、最小、最大和差值(最大值與最小值之差), # 106.6 表示在應用程序啟動 106.6 毫秒后,啟動了該GC線程 [GC Worker Start (ms): Min: 106.6, Avg: 106.7, Max: 106.7, Diff: 0.1] # 根掃描時間的統計值 [Ext Root Scanning (ms): Min: 0.4, Avg: 0.4, Max: 0.5, Diff: 0.1, Sum: 3.3] # 更新記憶集(Remember Set)的耗時。 # 記憶集是G1中維護的一個數據結構,簡稱RS。每一個G1區域都有一個RS與之關聯。由于G1回收時是按照區域 # 回收的,比如在回收區域A的對象時,很可能并不回收區域B的對象,為了回收區域A的對象,要掃描區域B甚 # 至整個堆來判定區域A中哪些對象不可達,這樣做的代價顯然很大。因此,G1在區域A的RS中,記錄了在區域 # A中被其他區域引用的對象,這樣在回收區域A時,只要將RS視為區域A根集的一部分即可,從而避免做整個堆 # 的的掃描。由于系統在運行過程中,對象之間的引用關系是可能時刻變化的,為了更高效地跟蹤這些引用關系, # 會將這些變化記錄在Update Buffers中。這里的Processed Buffers指的就是處理這個Update Buffers數據。 [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] # 掃描RS的時間 [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] # 在正式回收前,G1 會對被回收區域的對象進行疏散,即將存活對象放置在其他區域中,因此需要進行對象復制 [Object Copy (ms): Min: 0.2, Avg: 0.3, Max: 0.4, Diff: 0.2, Sum: 2.2] # 給出GC工程線程終止的信息,這里的終止時間是線程花在終止階段的耗時。在GC線程終止前,它們會檢查其 # 他GC線程的工作隊列,查看是否仍然還有對象引用沒有處理完,如果其他線程仍然有沒有處理完的數據,請求 # 終止的線程會幫助它盡快完成,隨后再嘗試終止。其中,Termination Attempts 展示了工作線程的終止次數。 [Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 1.0] [Termination Attempts: Min: 1, Avg: 2.9, Max: 6, Diff: 5, Sum: 23] # 顯示GC線程花費在其他任務中的耗時 [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] [GC Worker Total (ms): Min: 0.8, Avg: 0.8, Max: 0.9, Diff: 0.1, Sum: 6.6] # GC 工作線程的完成時間 [GC Worker End (ms): Min: 107.5, Avg: 107.5, Max: 107.5, Diff: 0.0] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] # 顯示清空 CardTable的時間,RS 就是依靠CardTable來記錄哪些是存活對象的 [Clear CT: 0.1 ms] # 顯示其他幾個任務的耗時,比如選擇CSet(Collection Sets,表示被選取的、將要被回收的區域的集合)的時 # 間、Ref Proc(處理弱引用、軟引用的時間)、Ref End(弱引用、軟引用入隊時間)和Free CSet(釋放被 # 回收的CSet中區域的時間,包括它們的RS) [Other: 0.8 ms] [Choose CSet: 0.0 ms] [Ref Proc: 0.7 ms] [Ref Enq: 0.0 ms] [Redirty Cards: 0.1 ms] [Humongous Register: 0.0 ms] [Humongous Reclaim: 0.0 ms] [Free CSet: 0.0 ms] [Eden: 2048.0K(14.0M)->0.0B(13.0M) Survivors: 0.0B->1024.0K Heap: 13.8M(40.0M)->2656.1K(40.0M)] [Times: user=0.00 sys=0.00, real=0.00 secs]
-XX:+UseG1GC
:啟用G1回收器
-XX:MaxGCPauseMillis
: 用于指定目標最大停頓時間。如果任何一次停頓時間超過這個設置值,G1就會嘗試調整新生代和老年代的比例、調整堆大小、調整晉升年齡等,試圖達到預設目標。對于性能調優來說,總是魚和熊掌不可兼得,如果停頓時間縮短,對于新生代來說,意味著很可能要增加新生代GC的次數。對于老年代來說,為了獲得短暫的停頓時間,在混合GC時,一次收集的區域數量也會變少,這樣無疑增加了進行FullGC的可能性。
-XX:ParallelGCThreads
: 設置并行回收時GC的工作線程數量。
-XX:InitatingHeapOccupancyPercent
: 指定當整個堆使用率達到多少時,觸發并發標記周期的執行,默認值是45.InitatingHeapOccupancyPercent
一旦設置,始終都不會被G1修改,這意味著G1不會試圖改變這個值來滿足MaxGCPauseMillis
的目標,那么引起FullGC的可能性也大大增加,反之,一個過小的InitatingHeapOccupancyPercent
值會使得并發標記周期執行非常頻繁,大量GC線程搶占CPU,導致應用程序的性能有所下降。
關于java中的G1回收器怎么用就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。