線上服務(wù)運(yùn)行緩慢,老大讓我進(jìn)行JVM參數(shù)調(diào)優(yōu)
哈嘍,大家好,我是指北君。
最近線上服務(wù)運(yùn)行比較緩慢,老大提出讓我進(jìn)行JVM優(yōu)化。GC的內(nèi)容很多,也不可能一時(shí)間全部都掌握,今天就要看看G1的一些知識(shí),還有調(diào)優(yōu)時(shí)可調(diào)整的參數(shù)。
1.G1簡(jiǎn)介
G1的全稱為 Garbage First Garbage Collector, 是一款內(nèi)置在HotSpot JVM中的服務(wù)端垃圾收集器。
G1是作為CMS的替代產(chǎn)品出現(xiàn)的,其目標(biāo)是在滿足最短時(shí)間停頓的同時(shí)達(dá)到一個(gè)高吞吐量,適用于多核處理器,大內(nèi)存容量的系統(tǒng)。
其特點(diǎn)如下:
- 短停頓時(shí)間且可控。對(duì)內(nèi)存進(jìn)行分區(qū),可以應(yīng)用在大內(nèi)存系統(tǒng)中,其使用了基于內(nèi)存的新生代收集和混合收集。
- 高吞吐量。設(shè)計(jì)了新的并發(fā)標(biāo)記線程,并發(fā)處理分區(qū)之間的引用關(guān)系,加快垃圾回收速度。
1.1分區(qū)
G1垃圾回收器將堆內(nèi)存劃分成固定大小的Region,下圖為G1內(nèi)存的分配示意圖,其中灰色格子代表一個(gè)region。
其中G1的分區(qū)可以分為4類:
- 自由分區(qū)(Free Heap Region, FHR)
- 新生代分區(qū)(Young Heap Region , YHR) 新生代也分為Eden 和Survivor區(qū)
- 大對(duì)象分區(qū)(Humongous Heap Region,HHR) 大對(duì)象區(qū)可分為 大對(duì)象頭分區(qū)和大對(duì)象連續(xù)分區(qū),大對(duì)象一般占Region的一半以上。
- 老生代分區(qū)(Old Heap Region,OHR)
Region可以在1MB~32MB,且為2的N次冪,設(shè)定分區(qū)大小有以下方法:
- 可以通過(guò)-XX:G1HeapReginotallow=<>來(lái)指定大小,默認(rèn)為0.
- 默認(rèn)情況下是將整個(gè)堆分為2048個(gè)Region。
1.1.1 新生代大小
新生代大小的設(shè)置如下:
- 如果設(shè)置了最大值 (MaxNewSize) 和最小值(NewSize), Xmn 等價(jià)于MaxNewSize
- 如果設(shè)置了最大值和最小值,又設(shè)置了NewRatio 則忽略NewRatio
- 如果沒(méi)有設(shè)置新生代最大值和最小值,但是設(shè)置了NewRatio 則新生代的最大值最小值是相同的,都是整個(gè)堆空間的 (NewRatio+1)
- 如果沒(méi)有設(shè)置新生代最大值和最小值,或者只設(shè)置了最大值或者最小值中的一個(gè),那么G1將根據(jù)參數(shù)G1MaxNewSizePrecent(默認(rèn)值60) 和 G1NewSizePercent (默認(rèn)值5)占整個(gè)堆空間的比例計(jì)算最大值和最小值。
關(guān)于堆大小的參數(shù)優(yōu)化:
- G1HeapRegionSize 可以指定堆的大小,可指定也可以有內(nèi)存管理啟發(fā)推斷分區(qū)大小。
- xms/xmx 指定堆空間的最小值/最大值, 一定要設(shè)置正確的值,否則會(huì)影響分區(qū)大小推斷。
- G1不要設(shè)置MaxNewSize,NewSize,Xmn, NewRatio,即不要顯示的設(shè)置年輕代的大小 。G1對(duì)內(nèi)存的管理不是連續(xù)的,所以即使重新分配一個(gè)堆分區(qū)的代價(jià)不大;G1的目標(biāo)滿足垃圾收集停頓,這需要G1根據(jù)停頓時(shí)間動(dòng)態(tài)調(diào)整收集的分區(qū),如果設(shè)置了固定的分區(qū)數(shù),G1不能調(diào)整新生代的大小,則不容易滿足停頓時(shí)間的需求。
- GCTimeRatio指的是GC與應(yīng)用程序之間的時(shí)間占比,默認(rèn)值是9,表示GC與程序的時(shí)間占比為90%,增大該值將減少GC占用時(shí)間,增大該值則動(dòng)態(tài)擴(kuò)展內(nèi)存會(huì)更容易發(fā)生。
2.G1 GC可優(yōu)化參數(shù)
G1提供了兩種GC模式,Young GC 和 Mixed GC 兩種GC都會(huì)有STW.
Young GC
主要是對(duì)Eden區(qū)進(jìn)行GC ,一般情況下,會(huì)在Eden Region使用達(dá)到最大閾值時(shí),空間內(nèi)存不夠用時(shí),觸發(fā)YoungGC。每次Young GC會(huì)回收所有的Eden 和Serviour區(qū),并且將存活對(duì)象復(fù)制到Old區(qū)以及一些Survivor區(qū)。
Mixed GC
Mixed GC 會(huì)選取(并發(fā)標(biāo)記)所有的 Young Region和 回收收益較高的一些 Old Region, 然后進(jìn)行年輕代回收算法。
混合回收分為兩個(gè)階段。
- 并發(fā)標(biāo)記
- 垃圾回收
其中并發(fā)標(biāo)記階段可以分為以下幾個(gè)子階段:
- 初始標(biāo)記子階段:標(biāo)記所有直接可達(dá)的根對(duì)象,此階段會(huì)STW,
- 并發(fā)標(biāo)記子階段:YoungGC 執(zhí)行完成之后,如果滿足并發(fā)標(biāo)記的的條件(已分配及將要分配的內(nèi)存占總內(nèi)存的比例超過(guò)閾值之后),就進(jìn)行并發(fā)標(biāo)記,其中-XX:ConcGCThreads 控制并發(fā)標(biāo)記線程數(shù)量,一個(gè)線程每次掃描一個(gè)Region。此時(shí)標(biāo)記存活對(duì)象,
- 再標(biāo)記子階段:找出所有未被訪問(wèn)的存活對(duì)象,此過(guò)程為并發(fā)執(zhí)行,并且會(huì)有STW,其中-XX:ParallelGCThreads可指定GC暫停時(shí)可用的GC線程數(shù)。
- 清理子階段:需要STW,存活對(duì)象計(jì)數(shù),整理標(biāo)記位圖,釋放完全空閑的分區(qū)。
混合回收階段的參數(shù)優(yōu)化:
- 參數(shù)InitiatingHeapOccupancyPercent(IHOP),默認(rèn)值時(shí)45, 此值時(shí)啟動(dòng)并發(fā)標(biāo)記的先決條件,只有已分配內(nèi)存占總空間超過(guò)45%之后,才會(huì)啟動(dòng)并發(fā)標(biāo)記任務(wù)。增加此值,將導(dǎo)致并發(fā)標(biāo)記可能花費(fèi)更多的時(shí)間,也會(huì)讓YGC或者M(jìn)ixedGC時(shí)收集的分區(qū)變少,這樣就會(huì)導(dǎo)致更多的Full GC。這個(gè)值可以根據(jù)整體應(yīng)用占用的平均內(nèi)存來(lái)設(shè)置,可以把該值設(shè)置的比平均內(nèi)存稍微高一點(diǎn)。IHOP的設(shè)置效果很明顯,但是要設(shè)置合理的值并不容易,需要更多的性能測(cè)試來(lái)判斷。
- 參數(shù)G1ReservePercent, 默認(rèn)值是10,如果GC晉升失敗導(dǎo)致FullGC,則可以調(diào)大該值
- 參數(shù)ConcGCThreads為并發(fā)線程數(shù),默認(rèn)值為0,如果未設(shè)置,可以動(dòng)態(tài)調(diào)整,并且使用ParallelGCThreads為依據(jù)來(lái)推斷,如果并發(fā)標(biāo)記耗時(shí)較大,可以增大并發(fā)線程數(shù)。
- HeapSizePerGCThread 默認(rèn)為64M,表示每64M分配一個(gè)線程
- 參數(shù)UseDynamicNumberOfGCThreads,默認(rèn)為false,設(shè)為true表示可以動(dòng)態(tài)調(diào)整線程數(shù),調(diào)整范圍會(huì)根據(jù)最大線程數(shù),HeapSizePerGCThread確定。
- 參數(shù)GCDrainStackTargetSize,默認(rèn)值為64,表示并發(fā)標(biāo)記子階段,一次標(biāo)記最多標(biāo)記的最多對(duì)象個(gè)數(shù)。
- 參數(shù)GCMixedGCLiveThresholdPercent 默認(rèn)值85,用于判斷分區(qū)能否被加入到CSet中,低于該值將會(huì)被加入。
- 參數(shù)G1HeapWastePercent 默認(rèn)值5,即當(dāng)Cset中可回收空間 占總空間的比例大于G1HeapWastePercent才會(huì)開(kāi)始混合回收。
- 參數(shù)G1MixedGCCountTarget,默認(rèn)值為8,這個(gè)參數(shù)越大,收集老年代的分區(qū)越少,反之收集的分區(qū)就越多,盡量保持老年代分區(qū)在Cset中的比例超過(guò)1/G1MixedGCCountTarget。
- 參數(shù)G1OldCSetRegionThresholdPercent 默認(rèn)值為10,表示最多收集10%的分區(qū)。
- 參數(shù)G1ConcMarkStepDurationMillis 默認(rèn)值為10,表示每個(gè)并發(fā)標(biāo)記子階段最多執(zhí)行10ms
FullGC發(fā)生之后,基本都是串行回收. 如果不幸發(fā)生了FullGC, 那么我們能做的就是盡量讓FullGC盡快完成,然后降低其頻率。但是通常情況下,比較固定且較長(zhǎng)時(shí)間間隔的FullGC是被允許的。
那么FullGC相關(guān)也是有一些優(yōu)化調(diào)整的地方:
- 使用參數(shù)MinHeapFreeRatio 用于判斷是否可以擴(kuò)展堆空間,增大該值擴(kuò)展的概率就會(huì)變小。
- MaxHeapFreeRatio 判斷是否可以收縮空間,增大該值收縮的概率也會(huì)變小。
- MarkSweepAlwaysCompactCount 默認(rèn)值為4,這個(gè)值表示經(jīng)過(guò)一定次數(shù)的GC之后,允許當(dāng)前區(qū)域中一定比例的死亡對(duì)象當(dāng)作存活對(duì)象處理,暫時(shí)不回收,從而加快FullGC的處理流程。這個(gè)比例可以使用MarkSweepDeadRatio來(lái)修改,默認(rèn)值為5.
總結(jié)
以上是一些優(yōu)化參數(shù)的使用,至于具體調(diào)優(yōu)的目的要根據(jù)我們各個(gè)程序的要求。一般而言需要滿足最大的吞吐量和最小的暫停時(shí)間,GC頻率盡量低,堆空間的有效利用率高等。可調(diào)整的部分有內(nèi)存參數(shù)的優(yōu)化,引用的處理(Rset),并發(fā)標(biāo)記(Mark),垃圾回收部分。
Oracle官方有一些推薦調(diào)優(yōu)的方向:
- 針對(duì)年輕代的設(shè)置,盡量避免明確的設(shè)置年輕代的大小(使用-Xmn,-XX:NewRatio等),固定的年輕代大小會(huì)覆蓋最小停頓時(shí)間的目標(biāo)。
- 對(duì)于暫停時(shí)間的目標(biāo),我們需要考慮平衡延遲和吞吐量,兩者不可兼得,所以需要找到一個(gè)最佳的平衡點(diǎn)。
- 混合回收階段的優(yōu)化參數(shù)可以考慮率使用 -XX:InitiatingHeapOccupancyPercent 修改內(nèi)存占用比(具體可以參考前文), -XX:G1MixedGCLiveThresholdPercent 和 -XX:G1HeapWastePercent 改變混合垃圾回收的策略,-XX:G1MixedGCCountTarget 和 -XX:G1OldCSetRegionThresholdPercent 調(diào)整老年代在CSet中的占比
參考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html