讀全文時(shí)間大約需要15分鐘。
最近處理ES的問題,ES默認(rèn)GC方式是CMS。學(xué)習(xí)完JVM的G1GC后最終決定將ES集群GC方式調(diào)整為G1GC。
垃圾優(yōu)先型垃圾回收器 (G1 GC) 是適用于 Java HotSpot VM 的低暫停、服務(wù)器風(fēng)格的分代式垃圾回收器。G1 GC 使用并發(fā)和并行階段實(shí)現(xiàn)其目標(biāo)暫停時(shí)間,并保持良好的吞吐量。當(dāng) G1 GC 確定有必要進(jìn)行垃圾回收時(shí),它會(huì)先收集存活數(shù)據(jù)最少的區(qū)域(垃圾優(yōu)先)。
G1提供了兩種GC模式,Young GC和Mixed GC,兩種都是完全Stop The World的。 * Young GC:選定所有年輕代里的Region。通過控制年輕代的region個(gè)數(shù),即年輕代內(nèi)存大小,來控制young GC的時(shí)間開銷。 * Mixed GC:選定所有年輕代里的Region,外加根據(jù)global concurrent marking統(tǒng)計(jì)得出收集收益高的若干老年代Region。在用戶指定的開銷目標(biāo)范圍內(nèi)盡可能選擇收益高的老年代Region。
垃圾回收器 (GC) 是一個(gè)內(nèi)存管理工具。G1 GC 通過以下操作實(shí)現(xiàn)自動(dòng)內(nèi)存管理:
將對象分配給年輕代,并將老化對象晉升到老年代。
通過并發(fā)(并行)標(biāo)記階段,查找老年代中的存活對象。當(dāng)總的 Java 堆占用率超過默認(rèn)的閾值時(shí),Java HotSpot VM 將觸發(fā)標(biāo)記階段。
通過并行復(fù)制壓縮存活對象,恢復(fù)空閑內(nèi)存。
現(xiàn)在,我們來看看如何針對評估、分析和性能來調(diào)整和調(diào)優(yōu) G1 GC。我們假定您對 Java 垃圾回收有基本的了解。
G1 GC 是區(qū)域化、分代式垃圾回收器,這意味著 Java 對象堆(堆)被劃分成大小相同的若干區(qū)域。啟動(dòng)時(shí),Java 虛擬機(jī) (JVM) 會(huì)設(shè)置區(qū)域大小。區(qū)域大小從 1 MB 到 32 MB 不等,具體取決于堆大小。目標(biāo)是產(chǎn)生不超過 2048 個(gè)區(qū)域。Eden、存活空間和老年代是一系列不連續(xù)的邏輯區(qū)域。
G1 GC 有一個(gè)力求達(dá)到的暫停時(shí)間目標(biāo)(軟實(shí)時(shí))。在年輕代回收期間,G1 GC 會(huì)調(diào)整其年輕代空間(eden 和存活空間大小)以滿足軟實(shí)時(shí)目標(biāo)。在混合回收期間,G1 GC 會(huì)根據(jù)混合垃圾回收的目標(biāo)次數(shù)調(diào)整所回收的舊區(qū)域數(shù)量,并調(diào)整堆的每個(gè)區(qū)域中存活對象的百分比,以及總體可接受的堆廢物百分比。
G1 GC 將一組或多組區(qū)域(稱為回收集 (CSet))中的存活對象以增量、并行的方式復(fù)制到不同的新區(qū)域來實(shí)現(xiàn)壓縮,從而減少堆碎片。目標(biāo)是從可回收空間最多的區(qū)域開始,盡可能回收更多的堆空間,同時(shí)盡可能不超出暫停時(shí)間目標(biāo)(垃圾優(yōu)先)。
G1 GC 使用獨(dú)立的記憶集 (RSet) 跟蹤對區(qū)域的引用。獨(dú)立的 RSet 可以并行、獨(dú)立地回收區(qū)域,因?yàn)橹恍枰獙^(qū)域(而不是整個(gè)堆)的 RSet 進(jìn)行區(qū)域引用掃描。G1 GC 使用后寫屏障記錄堆的更改和更新 RSet。
垃圾回收階段
除了構(gòu)成停頓 (STW) 年輕代和混合垃圾回收的疏散暫停(如下所述),G1 GC 還具有并行、并發(fā)和多階段標(biāo)記周期。G1 GC 使用初始快照 (SATB) 算法,在標(biāo)記周期之初為堆中的存活對象集創(chuàng)建快照。存活對象集包括快照中的存活對象,以及標(biāo)記周期開始以來所分配的對象。G1 GC 標(biāo)記算法使用預(yù)寫屏障記錄和標(biāo)記作為邏輯快照一部分的對象。
年輕代垃圾回收
G1 GC 可滿足來自被添加到 eden 區(qū)域集的區(qū)域的大多數(shù)分配請求。在年輕代垃圾回收期間,G1 GC 會(huì)同時(shí)回收 eden 區(qū)域和上次垃圾回收的存活區(qū)域。Eden 和存活區(qū)的存活對象將被復(fù)制或疏散到新的區(qū)域集。特定對象的目標(biāo)區(qū)域取決于對象的年齡;足夠老的對象疏散到老年代區(qū)域(也就晉級);否則疏散到存活區(qū),并將包含在下一次年輕代或混合垃圾回收的 CSet 中。
混合垃圾回收
成功完成并發(fā)標(biāo)記周期后,G1 GC 從執(zhí)行年輕代垃圾回收切換為執(zhí)行混合垃圾回收。在混合垃圾回收期間,G1 GC 可以將一些舊的區(qū)域添加到 eden 和存活區(qū)供將來回收。所添加舊區(qū)域的確切數(shù)量由一系列標(biāo)志控制。關(guān)于標(biāo)志的信息,將在后面討論(請參見"掌握混合垃圾回收")。G1 GC 回收了足夠的舊區(qū)域后(經(jīng)過多次混合垃圾回收),G1 將恢復(fù)執(zhí)行年輕代垃圾回收,直到下一個(gè)標(biāo)記周期完成。
標(biāo)記周期的各個(gè)階段
標(biāo)記周期包括以下幾個(gè)階段:
初始標(biāo)記階段:在此階段,G1 GC 對根進(jìn)行標(biāo)記。該階段與常規(guī)的 (STW) 年輕代垃圾回收密切相關(guān)。
根區(qū)域掃描階段:G1 GC 在初始標(biāo)記的存活區(qū)掃描對老年代的引用,并標(biāo)記被引用的對象。該階段與應(yīng)用程序(非 STW)同時(shí)運(yùn)行,并且只有完成該階段后,才能開始下一次 STW 年輕代垃圾回收。
并發(fā)標(biāo)記階段:G1 GC 在整個(gè)堆中查找可訪問的(存活的)對象。該階段與應(yīng)用程序同時(shí)運(yùn)行,可以被 STW 年輕代垃圾回收中斷。
重新標(biāo)記階段:該階段是 STW 回收,幫助完成標(biāo)記周期。G1 GC 清空 SATB 緩沖區(qū),跟蹤未被訪問的存活對象,并執(zhí)行引用處理。
清理階段:在這個(gè)最后階段,G1 GC 執(zhí)行統(tǒng)計(jì)和 RSet 凈化的 STW 操作。在統(tǒng)計(jì)期間,G1 GC 會(huì)識別完全空閑的區(qū)域和可供進(jìn)行混合垃圾回收的區(qū)域。清理階段在將空白區(qū)域重置并返回到空閑列表時(shí)為部分并發(fā)。
重要的默認(rèn)值
G1 GC 是自適應(yīng)的垃圾回收器,提供了若干默認(rèn)設(shè)置,使其無需修改即可高效地工作。以下是重要選項(xiàng)及其默認(rèn)值的列表。此列表適用于最新的 Java HotSpot VM build 24。您可以通過在 JVM 命令行輸入下列選項(xiàng)和已更改的設(shè)置,根據(jù)您的應(yīng)用程序性能需求調(diào)整和調(diào)優(yōu) G1 GC。
-XX:G1HeapRegionSize=n
設(shè)置的 G1 區(qū)域的大小。值是 2 的冪,范圍是 1 MB 到 32 MB 之間。目標(biāo)是根據(jù)最小的 Java 堆大小劃分出約 2048 個(gè)區(qū)域。
-XX:MaxGCPauseMillis=200
為所需的最長暫停時(shí)間設(shè)置目標(biāo)值。默認(rèn)值是 200 毫秒。指定的值不適用于您的堆大小。
-XX:G1NewSizePercent=5
設(shè)置要用作年輕代大小最小值的堆百分比。默認(rèn)值是 Java 堆的 5%。這是一個(gè)實(shí)驗(yàn)性的標(biāo)志。有關(guān)示例,請參見"如何解鎖實(shí)驗(yàn)性虛擬機(jī)標(biāo)志"。此設(shè)置取代了 -XX:DefaultMinNewGenPercent 設(shè)置。Java HotSpot VM build 23 中沒有此設(shè)置。
-XX:G1MaxNewSizePercent=60
設(shè)置要用作年輕代大小最大值的堆大小百分比。默認(rèn)值是 Java 堆的 60%。這是一個(gè)實(shí)驗(yàn)性的標(biāo)志。有關(guān)示例,請參見"如何解鎖實(shí)驗(yàn)性虛擬機(jī)標(biāo)志"。此設(shè)置取代了 -XX:DefaultMaxNewGenPercent 設(shè)置。Java HotSpot VM build 23 中沒有此設(shè)置。
-XX:ParallelGCThreads=n
設(shè)置 STW 工作線程數(shù)的值。將 n 的值設(shè)置為邏輯處理器的數(shù)量。n 的值與邏輯處理器的數(shù)量相同,最多為 8。
如果邏輯處理器不止八個(gè),則將 n 的值設(shè)置為邏輯處理器數(shù)的 5/8 左右。這適用于大多數(shù)情況,除非是較大的 SPARC 系統(tǒng),其中 n 的值可以是邏輯處理器數(shù)的 5/16 左右。
-XX:ConcGCThreads=n
設(shè)置并行標(biāo)記的線程數(shù)。將 n 設(shè)置為并行垃圾回收線程數(shù) (ParallelGCThreads) 的 1/4 左右。
-XX:InitiatingHeapOccupancyPercent=45
設(shè)置觸發(fā)標(biāo)記周期的 Java 堆占用率閾值。默認(rèn)占用率是整個(gè) Java 堆的 45%。
-XX:G1MixedGCLiveThresholdPercent=65
為混合垃圾回收周期中要包括的舊區(qū)域設(shè)置占用率閾值。默認(rèn)占用率為 65%。這是一個(gè)實(shí)驗(yàn)性的標(biāo)志。有關(guān)示例,請參見"如何解鎖實(shí)驗(yàn)性虛擬機(jī)標(biāo)志"。此設(shè)置取代了 -XX:G1OldCSetRegionLiveThresholdPercent 設(shè)置。Java HotSpot VM build 23 中沒有此設(shè)置。
-XX:G1HeapWastePercent=10
設(shè)置您愿意浪費(fèi)的堆百分比。如果可回收百分比小于堆廢物百分比,Java HotSpot VM 不會(huì)啟動(dòng)混合垃圾回收周期。默認(rèn)值是 10%。Java HotSpot VM build 23 中沒有此設(shè)置。
-XX:G1MixedGCCountTarget=8
設(shè)置標(biāo)記周期完成后,對存活數(shù)據(jù)上限為 G1MixedGCLIveThresholdPercent 的舊區(qū)域執(zhí)行混合垃圾回收的目標(biāo)次數(shù)。默認(rèn)值是 8 次混合垃圾回收?;旌匣厥盏哪繕?biāo)是要控制在此目標(biāo)次數(shù)以內(nèi)。Java HotSpot VM build 23 中沒有此設(shè)置。
-XX:G1OldCSetRegionThresholdPercent=10
設(shè)置混合垃圾回收期間要回收的最大舊區(qū)域數(shù)。默認(rèn)值是 Java 堆的 10%。Java HotSpot VM build 23 中沒有此設(shè)置。
-XX:G1ReservePercent=10
設(shè)置作為空閑空間的預(yù)留內(nèi)存百分比,以降低目標(biāo)空間溢出的風(fēng)險(xiǎn)。默認(rèn)值是 10%。增加或減少百分比時(shí),請確保對總的 Java 堆調(diào)整相同的量。Java HotSpot VM build 23 中沒有此設(shè)置。
如何解鎖實(shí)驗(yàn)性虛擬機(jī)標(biāo)志
要更改實(shí)驗(yàn)性標(biāo)志的值,必須先對其解鎖。解鎖方法是:在命令行中的實(shí)驗(yàn)性標(biāo)志前,顯式地設(shè)置 -XX:+UnlockExperimentalVMOptions。例如:
> java -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=10 -XX:G1MaxNewSizePercent=75 G1test.jar
評估和微調(diào) G1 GC 時(shí),請記住以下建議:
· 年輕代大小:避免使用 -Xmn 選項(xiàng)或 -XX:NewRatio 等其他相關(guān)選項(xiàng)顯式設(shè)置年輕代大小。固定年輕代的大小會(huì)覆蓋暫停時(shí)間目標(biāo)。
· 暫停時(shí)間目標(biāo):每當(dāng)對垃圾回收進(jìn)行評估或調(diào)優(yōu)時(shí),都會(huì)涉及到延遲與吞吐量的權(quán)衡。G1 GC 是增量垃圾回收器,暫停統(tǒng)一,同時(shí)應(yīng)用程序線程的開銷也更多。G1 GC 的吞吐量目標(biāo)是 90% 的應(yīng)用程序時(shí)間和 10%的垃圾回收時(shí)間。如果將其與 Java HotSpot VM 的吞吐量回收器相比較,目標(biāo)則是 99% 的應(yīng)用程序時(shí)間和 1% 的垃圾回收時(shí)間。因此,當(dāng)您評估 G1 GC 的吞吐量時(shí),暫停時(shí)間目標(biāo)不要太嚴(yán)苛。目標(biāo)太過嚴(yán)苛表示您愿意承受更多的垃圾回收開銷,而這會(huì)直接影響到吞吐量。當(dāng)您評估 G1 GC 的延遲時(shí),請?jiān)O(shè)置所需的(軟)實(shí)時(shí)目標(biāo),G1 GC 會(huì)盡量滿足。副作用是,吞吐量可能會(huì)受到影響。
· 掌握混合垃圾回收:當(dāng)您調(diào)優(yōu)混合垃圾回收時(shí),請嘗試以下選項(xiàng)。有關(guān)這些選項(xiàng)的信息,請參見"重要的默認(rèn)值":
-XX:InitiatingHeapOccupancyPercent
· 用于更改標(biāo)記閾值。
-XX:G1MixedGCLiveThresholdPercent 和 -XX:G1HeapWastePercent
· 當(dāng)您想要更改混合垃圾回收決定時(shí)。
-XX:G1MixedGCCountTarget 和 -XX:G1OldCSetRegionThresholdPercent
· 當(dāng)您想要調(diào)整舊區(qū)域的 CSet 時(shí)。
有關(guān)溢出和用盡的日志消息
當(dāng)您在日志中看到目標(biāo)空間溢出/用盡的消息時(shí),意味著 G1 GC 沒有足夠的內(nèi)存,供存活者和/或晉升對象使用。Java 堆不能擴(kuò)展,因?yàn)橐堰_(dá)到最大值。示例消息:
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]
要緩解此問題,請嘗試以下調(diào)整:
增加 -XX:G1ReservePercent 選項(xiàng)的值(并相應(yīng)增加總的堆大小),為"目標(biāo)空間"增加預(yù)留內(nèi)存量。
通過減少 -XX:InitiatingHeapOccupancyPercent 提前啟動(dòng)標(biāo)記周期。
您也可以通過增加 -XX:ConcGCThreads 選項(xiàng)的值來增加并行標(biāo)記線程的數(shù)目。
有關(guān)這些選項(xiàng)的描述,請參見"重要的默認(rèn)值"。
參考資料:http://dwz.win/9Ze
JVM 調(diào)優(yōu)是一個(gè)很大的話題,在回答“如何進(jìn)行 JVM 調(diào)優(yōu)?”之前,首先我們要回答一個(gè)更為關(guān)鍵的問題,那就是,我們?yōu)槭裁匆M(jìn)行 JVM 調(diào)優(yōu)?
只有知道了為什么要進(jìn)行 JVM 調(diào)優(yōu)之后,你才能準(zhǔn)確的回答出來如何進(jìn)行 JVM 調(diào)優(yōu)?
要進(jìn)行 JVM 調(diào)優(yōu)無非就是以下兩種情況:
所以,針對不同的 JVM 調(diào)優(yōu)的手段和側(cè)重點(diǎn)也是不同的。
總的來說,JVM 進(jìn)行調(diào)優(yōu)的流程如下:
具體來說它們的執(zhí)行如下。
先確定是目標(biāo)驅(qū)動(dòng)型的 JVM 調(diào)優(yōu),還是問題驅(qū)動(dòng)型的 JVM 調(diào)優(yōu)。
如果是目標(biāo)性的 JVM 調(diào)優(yōu),那么 JVM 調(diào)優(yōu)實(shí)現(xiàn)思路就比較簡單了,如:
如果是以問題驅(qū)動(dòng)的 JVM 調(diào)優(yōu),那就要先分析問題是什么,然后再進(jìn)行下一步的調(diào)優(yōu)了。
我們可以借助于目前主流的監(jiān)控工具 Prometheus + Grafana 和 JDK 自帶的命令行工具,如 jps、jstat、jinfo、jstack 等進(jìn)行 JVM 運(yùn)行情況的分析。
主要分析的點(diǎn)是 Young GC 和 Full GC 的頻率,以及垃圾回收的執(zhí)行時(shí)間。
常見的 JVM 調(diào)優(yōu)參數(shù)有以下幾個(gè):
JVM 參數(shù)調(diào)整之后,我們要通過壓力測試來觀察 JVM 參數(shù)調(diào)整前和調(diào)整后的差別,以確認(rèn)調(diào)整后的效果。
在確認(rèn)了 JVM 參數(shù)調(diào)整后的效果滿足需求之后,就可以將 JVM 的參數(shù)配置應(yīng)用與生產(chǎn)環(huán)境了。
除了以上常見的 JVM 調(diào)優(yōu)參數(shù)之外,還有沒有其他重要的參數(shù)設(shè)置呢?
本文已收錄到我的面試小站 [www.javacn.site](https://www.javacn.site),其中包含的內(nèi)容有:Redis、JVM、并發(fā)、并發(fā)、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、設(shè)計(jì)模式、消息隊(duì)列等模塊。