1. 程式人生 > >java架構之路-(JVM優化與原理)JVM之G1回收器和常見引數配置

java架構之路-(JVM優化與原理)JVM之G1回收器和常見引數配置

  過去的幾天裡,我把JVM內部的垃圾回收演算法和垃圾回收器。還剩下最後一個G1回收器沒有說,我們今天數一下G1回收器和常見的引數配置。

G1回收器

G1 (Garbage-First)是一款面向伺服器的垃圾收集器,主要針對配備多顆處理器及大容量記憶體的機器,以極高概率滿足GC停頓時間要求的同時,還具備高吞吐量效能特徵。我們會想一下我們上次課說的parNew+CMS回收器已經很厲害了,但是我們的伺服器是那種超大記憶體的伺服器呢?比如記憶體是32G的,可能最後我們的堆記憶體需要分配到十多個G,我們用parNew+CMS對於STW時間還是很長的,需要清理的越多,時間消耗越長啊,但是我們的G1垃圾回收器可以做到限制時間來收集,我們先看一下G1的模型圖。

來解釋一下這個圖,G1垃圾回收器,會把你的堆記憶體分為大小相等的獨立區域(Region),JVM最多可以分配2048個Region,一般Region大小等於堆大小除以2048,比如堆大小為4096M,則Region大小為2M,當然也可以 用引數"-XX:G1HeapRegionSize"手動指定Region大小,每個Region的狀態不確定,可能是Eden區域,也可能是Old區域,不需要原有的聯絡設定,這裡說一下以前沒有提到的Humongous區域,他是用來儲存大物件的,大物件的判定規則就是一個大物件超過了一個Region大小的50%,比如按 照上面算的,每個Region是2M,只要一個大物件超過了1M,就會被放入Humongous中視物件大小而定格子的連續數目。

預設年輕代對堆記憶體的佔比是5%,如果堆大小為4096M,那麼年輕代佔據200MB左右的記憶體, 對應大概是100個Region,可以通過“-XX:G1NewSizePercent”設定新生代初始佔比,在系統 執行中,JVM會不停的給年輕代增加更多的Region,但是最多新生代的佔比不會超過60%,可以 通過“-XX:G1MaxNewSizePercent”調整。年輕代中的Eden和Survivor對應的region也跟之前 一樣,預設8:1:1,假設年輕代現在有1000個region,eden區對應800個,s0對應100個,s1對應 100個。

G1收集器一次GC的運作過程大致分為以下幾個步驟:
初始標記(initial mark,STW):暫停所有的其他執行緒,並記錄下gc roots直接能引用的物件,速度很快 ; 併發標記(Concurrent Marking):同CMS的併發標記 最終標記(Remark,STW):同CMS的重新標記 篩選回收(Cleanup,STW):篩選回收階段首先對各個Region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間(可以用JVM引數 -XX:MaxGCPauseMillis指定)來制定回收計劃,就是說我們希望每次STW時間只是100ms,我們可以指定垃圾回收完成時間為100ms,但是100ms是不一定完全回收完成的,我們會按照每一個格子的回收消耗時間來排序,由小到大進行回收處理,直到時間為100ms為止。
這裡的回收演算法是複製演算法,也就是我們把每一個小格子的物件進行處理,把有用的物件複製到空格子內,把原有格子清空,需要注意一點的是不需要再次整理記憶體碎片,舉一個例項,Region1和Region2都需要回收,他們的大小都為2M,Region1有0.3M的物件是有用的,Region2的物件有0.6是有用,這時他們會共同放在一個空的小格子內佔用0.9M的大小。
與我們以前熟悉的YoungGC和OldGC所不同的是G1的回收器有三種GC,分別為: YoungGC: YoungGC並不是說現有的Eden區放滿了就會馬上觸發,G1會計算下現在Eden區回收大概要多久時間,如果回收時間遠遠小於引數 -XX:MaxGCPauseMills 設定的值,那麼增加年輕代的region,繼續給新物件存放,不會馬上做YoungGC,直到下一次Eden區放滿,G1計算回收時間接近引數 -XX:MaxGCPauseMills 設定的值,那麼就會觸發YoungGC

MixedGC:

不是FullGC,老年代的堆佔有率達到引數(-XX:InitiatingHeapOccupancyPercen)設定的值則觸發,回收所有的Young和部分Old(根據期望的GC停頓時間確定old區垃圾收集的優先順序)以及大物件區,正常情況G1的垃圾收集是先做MixedGC,主要使用複製演算法,需要把各個region中 存活的物件拷貝到別的region裡去,拷貝過程中如果發現沒有足夠的空region能夠承載拷貝物件 就會觸發一次Full GC

Full GC:

停止系統程式,然後採用單執行緒進行標記、清理和壓縮整理,好空閒出來一批Region來供下一次MixedGC使用,這個過程是非常耗時的。

JVM引數配置以及優化(JDK1.8)

棧相關

‐Xss->設定單個執行緒棧大小,比如-Xss512K,數值越小,一個執行緒棧裡能分配的棧幀就越少,說明可以開啟的執行緒數越多

方法區(元空間)

‐XX:MetaspaceSize->設定方法區的大小,也是觸發GC的閾值,比如‐XX:MetaspaceSize=256M

‐XX:MaxMetaspaceSiz->設定方法區的最大值,比如‐XX:MaxMetaspaceSize=256M 

堆相關

‐Xms->jvm啟動時分配的記憶體,比如‐Xms200m

‐Xmx->jvm執行過程中分配的最大記憶體,比如‐Xms500m

‐Xmn->設定年輕代大小,比如‐Xmn2g

‐XX:NewSize->設定年輕代大小 比如‐XX:NewSize=2g

‐XX:PretenureSizeThreshold->可以設定大物件的大小,比如‐XX:PretenureSizeThreshold=100000000 單位為btye。

‐XX:MaxTenuringThreshold->設定分代年齡,比如‐XX:MaxTenuringThreshold=10 預設為15。

‐XX:-HandlePromotionFailure->老年代分配擔保機制引數,1.8預設開啟。

‐XX:-UseAdaptiveSizePolicy->禁止JVM自動優化eden和Survivor預設比例8:1:1,反之JVM預設有這個引數‐XX:+UseAdaptiveSizePolicy,會導致這個比例自動變化。

‐XX:SurvivorRatio->設定Eden和Survivor大小比如 ‐XX:SurvivorRatio =8,注意Survivor區有兩個。表示Eden:Survivor=8:2,一個Survivor區佔整個年輕代的1/10。

‐XX:NewRatio->設定老年代和年輕代的比值大小 比如‐XX:NewRatio=4,表示年老代和年輕代比值為4:1。

回收器相關

Serial收集器

‐XX:+UseSerialGC->指定年輕代為Serial收集器

‐XX:+UseSerialOldGC->指定老年代為Serial收集器

ParNew收集器

‐XX:+UseParNewG->指定年輕代為ParNew收集器

Parallel Scavenge收集器

‐XX:+UseParallelGC->指定年輕代為Parallel收集器

‐XX:+UseParallelOldGC->指定老年代為Parallel收集器

‐XX:ParallelGCThreads->指定GC工作的執行緒數量

CMS收集器

‐XX:+UseConcMarkSweepGC->指定指定老年代為CMS收集器

‐XX:ConcGCThreads->併發的GC執行緒數

‐XX:+UseCMSCompactAtFullCollection->FullGC之後是否做壓縮整理(減少碎片)

‐XX:CMSFullGCsBeforeCompaction->多少次FullGC之後壓縮一次,預設是0,代表每次FullGC後都會壓縮一次,比如‐XX:CMSFullGCsBeforeCompaction=0 

‐XX:CMSInitiatingOccupancyFraction->當老年代使用達到該比例時會觸發FullGC(預設是92,這是百分比),比如‐XX:CMSInitiatingOccupancyFaction=92

‐XX:+UseCMSInitiatingOccupancyOnly->只使用設定的回收閾值(‐XX:CMSInitiatingOccupancyFraction設定的值),如果不指定,JVM僅在第一次使用設定值,後續則會自動調整

‐XX:+CMSScavengeBeforeRemark->在CMSGC前啟動一次minor gc,目的在於減少老年代對年輕代的引用,降低CMS GC的標記階段時的開銷,一般CMS的GC耗時80%都在 remark階段 

G1收集器

‐XX:+UseG1GC->開啟G1收集器

‐XX:G1HeapRegionSize->指定分割槽大小(1MB~32MB,且必須是2的冪),預設將整堆劃分為2048個分割槽

‐XX:MaxGCPauseMillis->目標暫停時間(預設200ms)

‐XX:G1NewSizePercent->新生代記憶體初始空間(預設整堆5%)

‐XX:G1MaxNewSizePercent->新生代記憶體最大空間

‐XX:TargetSurvivorRatio->Survivor區的填充容量(預設50%),Survivor區域裡的一批物件(年齡1+年齡2+年齡n的多個年齡物件)總和超過了Survivor區域的50%,此時就會把年齡n(含)以上的物件都放入老年代

‐XX:InitiatingHeapOccupancyPercent->老年代佔用空間達到整堆記憶體閾值(預設45%),則執行 新生代和老年代的混合收集(MixedGC),比如我們之前說的堆預設有2048個region,如果有接近 1000個region都是老年代的region,則可能就要觸發MixedGC了

‐XX:G1HeapWastePercent->預設5%,gc過程中空出來的region是否充足閾值,在混合回收的時候,對Region回收都是基於複製演算法進行的,都是把要回收的Region裡的存活物件放入其他 Region,然後這個Region中的垃圾物件全部清理掉,這樣的話在回收過程就會不斷空出來新的 Region,一旦空閒出來的Region數量達到了堆記憶體的5%,此時就會立即停止混合回收,意味著 本次混合回收就結束了。

‐XX:G1MixedGCLiveThresholdPercent->預設85%,region中的存活物件低於這個值時才會回收該region,如果超過這個值,存活物件過多,回收的的意義不大。

‐XX:G1MixedGCCountTarget->在一次回收過程中指定做幾次篩選回收(預設8次),在最後一個篩選回收階段可以回收一會,然後暫停回收,恢復系統執行,一會再開始回收,這樣可以讓系統不至於單次停頓時間過長。 

日誌調優相關

‐XX:+PrintGCDetails->列印GC日誌

‐XX:+PrintGCTimeStamps->列印GC時間

‐XX:+PrintGCDateStamps->列印GC日期

‐Xloggc->將GC日誌儲存為檔案,比如‐Xloggc:./gc.log

有興趣的小夥伴可以自學一下jmap ‐heap PID,jstat -gc PID(個人認為這個超級重要),javap -c ***.class,Jstack等調優命令,線上盡力別用jvisualvm命令,消耗效能,很多公司禁用jvisualvm命令

我們來回顧一下我們JVM都說了什麼知識點。

一,類載入過程:載入-驗證-準備-解析-初始化-使用-解除安裝

二,雙親委派機制。

三,記憶體執行模型(堆和棧)

四,記憶體分割槽老年代和年輕代,年輕代包含Eden區和Survivor區。

五,GC回收minor和fullGC,什麼時候會觸發fullGC,重點是物件動態年齡判斷和老年代擔保分配機制。

六,垃圾回收的演算法,三種,複製,標記清理,標記整理。

七,垃圾回收器五種,序列的Serial,並行的parNew,高CPU的Parallel,常用的CMS和大記憶體的G1。

八,常用命令。

很多都是孰能生巧的,細節的還有很多,JVM優化路我給你們指出了,剩下的還需要你們自己去探索,加油~!!!

再不會調優的可以來私信我,我可以嘗試為你提出免費除錯建議。