垃圾優先型垃圾回收器調優

瞭解如何針對評估、分析和性能來調整和調優 G1 GC。

垃圾優先型垃圾回收器 (G1 GC) 是適用於 Java HotSpot VM 的低暫停、服務器風格的分代式垃圾回收器。G1 GC 使用併發和並行階段實現其目標暫停時間,並保持良好的吞吐量。當 G1 GC 確定有必要進行垃圾回收時,它會先收集存活數據最少的區域(垃圾優先)。

垃圾回收器 (GC) 是一個內存管理工具。G1 GC 通過以下操作實現自動內存管理:

將對象分配給年輕代,並將老化對象晉升到老年代。
通過併發(並行)標記階段,查找老年代中的存活對象。當總的 Java 堆佔用率超過默認的閾值時,Java HotSpot VM 將觸發標記階段。
通過並行複製壓縮存活對象,恢復空閒內存。
現在,我們來看看如何針對評估、分析和性能來調整和調優 G1 GC。我們假定您對 Java 垃圾回收有基本的瞭解。

G1 GC 是區域化、分代式垃圾回收器,這意味着 Java 對象堆(堆)被劃分成大小相同的若干區域。啓動時,Java 虛擬機 (JVM) 會設置區域大小。區域大小從 1 MB 到 32 MB 不等,具體取決於堆大小。目標是產生不超過 2048 個區域。Eden、存活空間和老年代是一系列不連續的邏輯區域。

G1 GC 有一個力求達到的暫停時間目標(軟實時)。在年輕代回收期間,G1 GC 會調整其年輕代空間(eden 和存活空間大小)以滿足軟實時目標。在混合回收期間,G1 GC 會根據混合垃圾回收的目標次數調整所回收的舊區域數量,並調整堆的每個區域中存活對象的百分比,以及總體可接受的堆廢物百分比。

G1 GC 將一組或多組區域(稱爲回收集 (CSet))中的存活對象以增量、並行的方式複製到不同的新區域來實現壓縮,從而減少堆碎片。目標是從可回收空間最多的區域開始,儘可能回收更多的堆空間,同時儘可能不超出暫停時間目標(垃圾優先)。

G1 GC 使用獨立的記憶集 (RSet) 跟蹤對區域的引用。獨立的 RSet 可以並行、獨立地回收區域,因爲只需要對區域(而不是整個堆)的 RSet 進行區域引用掃描。G1 GC 使用後寫屏障記錄堆的更改和更新 RSet。

垃圾回收階段
除了構成停頓 (STW) 年輕代和混合垃圾回收的疏散暫停(如下所述),G1 GC 還具有並行、併發和多階段標記週期。G1 GC 使用初始快照 (SATB) 算法,在標記週期之初爲堆中的存活對象集創建快照。存活對象集包括快照中的存活對象,以及標記週期開始以來所分配的對象。G1 GC 標記算法使用預寫屏障記錄和標記作爲邏輯快照一部分的對象。

年輕代垃圾回收
G1 GC 可滿足來自被添加到 eden 區域集的區域的大多數分配請求。在年輕代垃圾回收期間,G1 GC 會同時回收 eden 區域和上次垃圾回收的存活區域。Eden 和存活區的存活對象將被複制或疏散到新的區域集。特定對象的目標區域取決於對象的年齡;足夠老的對象疏散到老年代區域(也就晉級);否則疏散到存活區,並將包含在下一次年輕代或混合垃圾回收的 CSet 中。

混合垃圾回收
成功完成併發標記週期後,G1 GC 從執行年輕代垃圾回收切換爲執行混合垃圾回收。在混合垃圾回收期間,G1 GC 可以將一些舊的區域添加到 eden 和存活區供將來回收。所添加舊區域的確切數量由一系列標誌控制。關於標誌的信息,將在後面討論(請參見“掌握混合垃圾回收”)。G1 GC 回收了足夠的舊區域後(經過多次混合垃圾回收),G1 將恢復執行年輕代垃圾回收,直到下一個標記週期完成。

標記週期的各個階段
標記週期包括以下幾個階段:

初始標記階段:在此階段,G1 GC 對根進行標記。該階段與常規的 (STW) 年輕代垃圾回收密切相關。
根區域掃描階段:G1 GC 在初始標記的存活區掃描對老年代的引用,並標記被引用的對象。該階段與應用程序(非 STW)同時運行,並且只有完成該階段後,才能開始下一次 STW 年輕代垃圾回收。
併發標記階段:G1 GC 在整個堆中查找可訪問的(存活的)對象。該階段與應用程序同時運行,可以被 STW 年輕代垃圾回收中斷。
重新標記階段:該階段是 STW 回收,幫助完成標記週期。G1 GC 清空 SATB 緩衝區,跟蹤未被訪問的存活對象,並執行引用處理。
清理階段:在這個最後階段,G1 GC 執行統計和 RSet 淨化的 STW 操作。在統計期間,G1 GC 會識別完全空閒的區域和可供進行混合垃圾回收的區域。清理階段在將空白區域重置並返回到空閒列表時爲部分併發。
重要的默認值
G1 GC 是自適應的垃圾回收器,提供了若干默認設置,使其無需修改即可高效地工作。以下是重要選項及其默認值的列表。此列表適用於最新的 Java HotSpot VM build 24。您可以通過在 JVM 命令行輸入下列選項和已更改的設置,根據您的應用程序性能需求調整和調優 G1 GC。

-XX:G1HeapRegionSize=n

設置的 G1 區域的大小。值是 2 的冪,範圍是 1 MB 到 32 MB 之間。目標是根據最小的 Java 堆大小劃分出約 2048 個區域。

-XX:MaxGCPauseMillis=200

爲所需的最長暫停時間設置目標值。默認值是 200 毫秒。指定的值不適用於您的堆大小。

-XX:G1NewSizePercent=5

設置要用作年輕代大小最小值的堆百分比。默認值是 Java 堆的 5%。這是一個實驗性的標誌。有關示例,請參見“如何解鎖實驗性虛擬機標誌”。此設置取代了 -XX:DefaultMinNewGenPercent 設置。Java HotSpot VM build 23 中沒有此設置。

-XX:G1MaxNewSizePercent=60

設置要用作年輕代大小最大值的堆大小百分比。默認值是 Java 堆的 60%。這是一個實驗性的標誌。有關示例,請參見“如何解鎖實驗性虛擬機標誌”。此設置取代了 -XX:DefaultMaxNewGenPercent 設置。Java HotSpot VM build 23 中沒有此設置。

-XX:ParallelGCThreads=n

設置 STW 工作線程數的值。將 n 的值設置爲邏輯處理器的數量。n 的值與邏輯處理器的數量相同,最多爲 8。

如果邏輯處理器不止八個,則將 n 的值設置爲邏輯處理器數的 5/8 左右。這適用於大多數情況,除非是較大的 SPARC 系統,其中 n 的值可以是邏輯處理器數的 5/16 左右。

-XX:ConcGCThreads=n

設置並行標記的線程數。將 n 設置爲並行垃圾回收線程數 (ParallelGCThreads) 的 1/4 左右。

-XX:InitiatingHeapOccupancyPercent=45

設置觸發標記週期的 Java 堆佔用率閾值。默認佔用率是整個 Java 堆的 45%。

-XX:G1MixedGCLiveThresholdPercent=65

爲混合垃圾回收週期中要包括的舊區域設置佔用率閾值。默認佔用率爲 65%。這是一個實驗性的標誌。有關示例,請參見“如何解鎖實驗性虛擬機標誌”。此設置取代了 -XX:G1OldCSetRegionLiveThresholdPercent 設置。Java HotSpot VM build 23 中沒有此設置。

-XX:G1HeapWastePercent=10

設置您願意浪費的堆百分比。如果可回收百分比小於堆廢物百分比,Java HotSpot VM 不會啓動混合垃圾回收週期。默認值是 10%。Java HotSpot VM build 23 中沒有此設置。

-XX:G1MixedGCCountTarget=8

設置標記週期完成後,對存活數據上限爲 G1MixedGCLIveThresholdPercent 的舊區域執行混合垃圾回收的目標次數。默認值是 8 次混合垃圾回收。混合回收的目標是要控制在此目標次數以內。Java HotSpot VM build 23 中沒有此設置。

-XX:G1OldCSetRegionThresholdPercent=10

設置混合垃圾回收期間要回收的最大舊區域數。默認值是 Java 堆的 10%。Java HotSpot VM build 23 中沒有此設置。

-XX:G1ReservePercent=10

設置作爲空閒空間的預留內存百分比,以降低目標空間溢出的風險。默認值是 10%。增加或減少百分比時,請確保對總的 Java 堆調整相同的量。Java HotSpot VM build 23 中沒有此設置。

如何解鎖實驗性虛擬機標誌
要更改實驗性標誌的值,必須先對其解鎖。解鎖方法是:在命令行中的實驗性標誌前,顯式地設置 -XX:+UnlockExperimentalVMOptions。例如:

java -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=10 -XX:G1MaxNewSizePercent=75 G1test.jar

建議
評估和微調 G1 GC 時,請記住以下建議:

年輕代大小:避免使用 -Xmn 選項或 -XX:NewRatio 等其他相關選項顯式設置年輕代大小。固定年輕代的大小會覆蓋暫停時間目標。
暫停時間目標:每當對垃圾回收進行評估或調優時,都會涉及到延遲與吞吐量的權衡。G1 GC 是增量垃圾回收器,暫停統一,同時應用程序線程的開銷也更多。G1 GC 的吞吐量目標是 90% 的應用程序時間和 10%的垃圾回收時間。如果將其與 Java HotSpot VM 的吞吐量回收器相比較,目標則是 99% 的應用程序時間和 1% 的垃圾回收時間。因此,當您評估 G1 GC 的吞吐量時,暫停時間目標不要太嚴苛。目標太過嚴苛表示您願意承受更多的垃圾回收開銷,而這會直接影響到吞吐量。當您評估 G1 GC 的延遲時,請設置所需的(軟)實時目標,G1 GC 會盡量滿足。副作用是,吞吐量可能會受到影響。
掌握混合垃圾回收:當您調優混合垃圾回收時,請嘗試以下選項。有關這些選項的信息,請參見“重要的默認值”:

-XX:InitiatingHeapOccupancyPercent
用於更改標記閾值。
-XX:G1MixedGCLiveThresholdPercent 和 -XX:G1HeapWastePercent
當您想要更改混合垃圾回收決定時。
-XX:G1MixedGCCountTarget 和 -XX:G1OldCSetRegionThresholdPercent
當您想要調整舊區域的 CSet 時。
有關溢出和用盡的日誌消息
當您在日誌中看到目標空間溢出/用盡的消息時,意味着 G1 GC 沒有足夠的內存,供存活者和/或晉升對象使用。Java 堆不能擴展,因爲已達到最大值。示例消息:

924.897: [GC pause (G1 Evacuation Pause) (mixed) (to-space exhausted), 0.1957310 secs]

924.897:[GC pause (G1 Evacuation Pause) (mixed) (to-space overflow), 0.1957310 secs]

要緩解此問題,請嘗試以下調整:

增加 -XX:G1ReservePercent 選項的值(並相應增加總的堆大小),爲“目標空間”增加預留內存量。

通過減少 -XX:InitiatingHeapOccupancyPercent 提前啓動標記週期。

您也可以通過增加 -XX:ConcGCThreads 選項的值來增加並行標記線程的數目。

有關這些選項的描述,請參見“重要的默認值”。

巨型對象和巨型分配
對於 G1 GC,任何超過區域一半大小的對象都被視爲“巨型對象”。此類對象直接被分配到老年代中的“巨型區域”。這些巨型區域是一個連續的區域集。StartsHumongous 標記該連續集的開始,ContinuesHumongous 標記它的延續。

在分配任何巨型區域之前,會檢查標記閾值,如有必要,還會啓動一個併發週期。

在清理階段或完整的垃圾回收週期內,標記週期結束時會清理死亡的巨型對象。

爲了減少複製開銷,巨型對象未包括在疏散暫停中。完整的垃圾回收週期會對巨型對象進行壓縮。

由於每個 StartsHumongous 和 ContinuesHumongous 區域集只包含一個巨型對象,所以沒有使用巨型對象的終點與上個區域的終點之間的空間(即巨型對象所跨的空間)。如果對象只是略大於堆區域大小的倍數,則此類未使用的空間可能會導致堆碎片化。

如果巨型分配導致連續的併發週期,並且此類分配導致老年代碎片化,請增加 -XX:G1HeapRegionSize,這樣一來,之前的巨型對象就不再是巨型對象了,而是採用常規的分配路徑。

總結
G1 GC 是區域化、並行-併發、增量式垃圾回收器,相比其他 HotSpot 垃圾回收器,可提供更多可預測的暫停。增量的特性使 G1 GC 適用於更大的堆,在最壞的情況下仍能提供不錯的響應。G1 GC 的自適應特性使 JVM 命令行只需要軟實時暫停時間目標的最大值以及 Java 堆大小的最大值和最小值,即可開始工作。

轉載自http://www.oracle.com/technetwork/cn/articles/java/g1gc-1984535-zhs.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章