1. 程式人生 > >java g1垃圾回收

java g1垃圾回收

Hotspot 架構圖
這裡寫圖片描述

紫色為調優的主要地方

G1 垃圾收集器(Garbage Collector)

G1 垃圾收集器

G1 (Garbage-First)是一款面向伺服器的垃圾收集器,主要針對配備多顆處理器及大容量記憶體的機器. 以極高概率滿足GC停頓時間要求的同時,還具備高吞吐量效能特徵. 在Oracle JDK 7 update 4 及以上版本中得到完全支援, 專為以下應用程式設計:

可以像CMS收集器一樣,GC操作與應用的執行緒一起併發執行
緊湊的空閒記憶體區間且沒有很長的GC停頓時間.
需要可預測的GC暫停耗時.
不想犧牲太多吞吐量效能.
啟動後不需要請求更大的Java堆.
G1的長期目標是取代CMS(Concurrent Mark-Sweep Collector, 併發標記-清除). 因為特性的不同使G1成為比CMS更好的解決方案. 一個區別是,G1是一款壓縮型的收集器.G1通過有效的壓縮完全避免了對細微空閒記憶體空間的分配,不用依賴於regions,這不僅大大簡化了收集器,而且還消除了潛在的記憶體碎片問題。除壓縮以外,G1的垃圾收集停頓也比CMS容易估計,也允許使用者自定義所希望的停頓引數(pause targets)

g1 垃圾收集器將記憶體模型改變了
這裡寫圖片描述
注意 G1分為兩個階段: 併發階段(concurrent, 與應用執行緒一起執行, 如: 細化 refinement、標記 marking、清理 cleanup) 和 並行階段(parallel, 多執行緒執行, 如: 停止所有JVM執行緒, stop the world). 而 FullGC(完整垃圾收集)仍然是單執行緒的, 但如果進行適當的調優,則應用程式應該能夠避免 full GC。

推薦使用 G1 的場景(Recommended Use Cases)

G1的首要目標是為需要大量記憶體的系統提供一個保證GC低延遲的解決方案. 也就是說堆記憶體在6GB及以上,穩定和可預測的暫停時間小於0.5秒.

如果應用程式具有如下的一個或多個特徵,那麼將垃圾收集器從CMS或ParallelOldGC切換到G1將會大大提升效能.

Full GC 次數太頻繁或者消耗時間太長.
物件分配的頻率或代數提升(promotion)顯著變化.
受夠了太長的垃圾回收或記憶體整理時間(超過0.5~1秒)
注意: 如果正在使用CMS或ParallelOldGC,而應用程式的垃圾收集停頓時間並不長,那麼繼續使用現在的垃圾收集器是個好主意. 使用最新的JDK時並不要求切換到G1收集器。

G1 的 GC 引數完全列表

選項/預設值 說明
-XX:+UseG1GC 使用 G1 (Garbage First) 垃圾收集器
-XX:MaxGCPauseMillis=n 設定最大GC停頓時間(GC pause time)指標(target). 這是一個軟性指標(soft goal), JVM 會盡量去達成這個目標.
-XX:InitiatingHeapOccupancyPercent=n 啟動併發GC週期時的堆記憶體佔用百分比. G1之類的垃圾收集器用它來觸發併發GC週期,基於整個堆的使用率,而不只是某一代記憶體的使用比. 值為 0 則表示”一直執行GC迴圈”. 預設值為 45.
-XX:NewRatio=n 新生代與老生代(new/old generation)的大小比例(Ratio). 預設值為 2.
-XX:SurvivorRatio=n eden/survivor 空間大小的比例(Ratio). 預設值為 8.
-XX:MaxTenuringThreshold=n 提升年老代的最大臨界值(tenuring threshold). 預設值為 15.
-XX:ParallelGCThreads=n 設定垃圾收集器在並行階段使用的執行緒數,預設值隨JVM執行的平臺不同而不同.
-XX:ConcGCThreads=n 併發垃圾收集器使用的執行緒數量. 預設值隨JVM執行的平臺不同而不同.
-XX:G1ReservePercent=n 設定堆記憶體保留為假天花板的總量,以降低提升失敗的可能性. 預設值是 10.
-XX:G1HeapRegionSize=n 使用G1時Java堆會被分為大小統一的的區(region)。此引數可以指定每個heap區的大小. 預設值將根據 heap size 算出最優解. 最小值為 1Mb, 最大值為 32Mb.

最佳實踐

在使用 G1 作為垃圾收集器時,你應該遵循下面這些最佳實踐的指導.

不要設定年輕代的大小(Young Generation Size)

假若通過 -Xmn 顯式地指定了年輕代的大小, 則會干擾到 G1收集器的預設行為.

G1在垃圾收集時將不再關心暫停時間指標. 所以從本質上說,設定年輕代的大小將禁用暫停時間目標.
G1在必要時也不能夠增加或者縮小年輕代的空間. 因為大小是固定的,所以對更改大小無能為力.
響應時間指標(Response Time Metrics)

設定 XX:MaxGCPauseMillis= 時不應該使用平均響應時間(ART, average response time) 作為指標,而應該考慮使用目標時間的90%或者更大作為響應時間指標. 也就是說90%的使用者(客戶端/?)請求響應時間不會超過預設的目標值. 記住,暫停時間只是一個目標,並不能保證總是得到滿足.

什麼是轉移失敗(Evacuation Failure)?

對 survivors 或 promoted objects 進行GC時如果JVM的heap區不足就會發生提升失敗(promotion failure). 堆記憶體不能繼續擴充,因為已經達到最大值了. 當使用 -XX:+PrintGCDetails 時將會在GC日誌中顯示 to-space overflow (to-空間溢位)。

這是很昂貴的操作!

GC仍繼續所以空間必須被釋放.
拷貝失敗的物件必須被放到正確的位置(tenured in place).
CSet指向區域中的任何 RSets 更新都必須重新生成(regenerated).
所有這些步驟都是代價高昂的.
如何避免轉移失敗(Evacuation Failure)

要避免避免轉移失敗, 考慮採納下列選項.

增加堆記憶體大小
增加 -XX:G1ReservePercent=n, 其預設值是 10.
G1建立了一個假天花板(false ceiling),在需要更大 ‘to-space’ 的情況下會嘗試從保留記憶體獲取(leave the reserve memory free).
更早啟動標記週期(marking cycle)
通過採用 -XX:ConcGCThreads=n 選項增加標記執行緒(marking threads)的數量.

設定日誌細節(Log Detail)

可以設定3種不同的日誌級別.

(1) -verbosegc (等價於 -XX:+PrintGC) 設定日誌級別為 好 fine.

日誌輸出示例

[GC pause (G1 Humongous Allocation) (young) (initial-mark) 24M- >21M(64M), 0.2349730 secs]
[GC pause (G1 Evacuation Pause) (mixed) 66M->21M(236M), 0.1625268 secs]
(2) -XX:+PrintGCDetails 設定日誌級別為 更好 finer. 使用此選項會顯示以下資訊:

每個階段的 Average, Min, 以及 Max 時間.
根掃描(Root Scan), RSet 更新(同時處理緩衝區資訊), RSet掃描(Scan), 物件拷貝(Object Copy), 終止(Termination, 包括嘗試次數).
還顯示 “other” 執行時間, 比如選擇 CSet, 引用處理(reference processing), 引用排隊(reference enqueuing) 以及釋放(freeing) CSet等.
顯示 Eden, Survivors 以及總的 Heap 佔用資訊(occupancies).
日誌輸出示例

[Ext Root Scanning (ms): Avg: 1.7 Min: 0.0 Max: 3.7 Diff: 3.7]
[Eden: 818M(818M)->0B(714M) Survivors: 0B->104M Heap: 836M(4096M)->409M(4096M)]
(3) -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest 設定日誌級別為 最好 finest. 和 finer 級別類似, 包含每個 worker 執行緒資訊.

[Ext Root Scanning (ms): 2.1 2.4 2.0 0.0
Avg: 1.6 Min: 0.0 Max: 2.4 Diff: 2.3]
[Update RS (ms): 0.4 0.2 0.4 0.0
Avg: 0.2 Min: 0.0 Max: 0.4 Diff: 0.4]
[Processed Buffers : 5 1 10 0
Sum: 16, Avg: 4, Min: 0, Max: 10, Diff: 10]
Determining Time

有兩個引數決定了GC日誌中列印的時間顯示形式.

(1) -XX:+PrintGCTimeStamps - 顯示從JVM啟動時算起的執行時間.

日誌輸出示例

1.729: [GC pause (young) 46M->35M(1332M), 0.0310029 secs]
(2) -XX:+PrintGCDateStamps - 在每條記錄前加上日期時間.

日誌輸出示例

2012-05-02T11:16:32.057+0200: [GC pause (young) 46M->35M(1332M), 0.0317225 secs]