GC調優方案,步驟三:嘗試調優
從上一篇分析gc資訊,我們總結了,做gc調優目標,就是減少gc頻率和耗時,以及減少程式暫停時間
(一)、首先簡單瞭解一下heap記憶體堆
年輕代:所有新生成的物件首先都是放在年輕代的。年輕代分三個區。一個Eden區,兩個 Survivor區(一般而言)。大部分物件在Eden區中生成。,Survivor的兩個區是對稱的,沒先後關係,所以同一個區中可能同時存在從Eden複製過來物件,和從前一個Survivor複製過來的物件,而複製到年老區的只有從第一個Survivor去過來的物件。而且,Survivor區總有一個是空的。同時,根據程式需要,Survivor區是可以配置為多個的(多於兩個),這樣可以增加物件在年輕代中的存在時間,減少被放到年老代的可能。
年老代:在年輕代中經歷了N次垃圾回收後仍然存活的物件,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。
持久代:用於存放靜態檔案,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者呼叫一些class,例如Hibernate 等,在這種時候需要設定一個比較大的持久代空間來存放這些執行過程中新增的類。持久代大小通過-XX:MaxPermSize=<N>進行設定。
堆大小定義如下:
總heap大小定義引數是:
預設是實體記憶體的1/64但小於1G。
-Xmx(最大堆大小)
-Xms(最小堆大小)
年輕代大小定義引數:
-Xmn(年輕代大小)
-XX:NewRaito(設定年輕代和年老代的比例值)
-XX:SurvivorRaito(設定年輕代中Eden區與Survivor區的比值,如此值為4,則Eden為4/6,兩個Survivor分別為1/6)
持久代大小定義引數:
-XX:PermSize(啟動時候持久代大小)
-XX:MaxPermSize(持久代最大可佔用大小)
沒有特定設定年老代大小的引數,其實可以計算得到。年老代=heap總大小-年輕代-持久代
(二)、物件移動規則
當Eden區滿時,還存活的物件將被複制到Survivor區(兩個中的一個),當這個 Survivor區滿時,此區的存活物件將被複制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的並且此時還存活的物件,將被複制“年老區(Tenured)”,所以年輕代物件到達年老代規則:
1. 經歷多次minor gc仍存活的物件,可通過MaxTenuringThreshold=<N>引數來控制,預設為15。
2. 物件在to space區放不下,就會直接放入舊生代
(三)、簡單瞭解各個區的gc
我們從上面知道,heap記憶體堆分為三大區,每個區域都有自己的gc方式,請看下面表
年輕代 |
年老代 |
持久代 |
||
GC型別 |
YGC |
FGC |
||
觸發條件 |
當eden空間已滿時觸發 |
年老代空間不足 呼叫System.GC, RMI等的定時觸發 |
持久代空間不足 |
|
觸發事件 |
清空Eden+from survivor中所有no ref的物件佔用的記憶體將eden+from sur中所有存活的物件copy到to sur中; 重新調整Eden 和from的大小(parallel GC會觸發此項) |
清空heap中no ref的物件 |
||
序列 |
適用環境 |
適用於單CPU、新生代空間較小及對暫停時間要求不是非常高的應用上,也是client級別(CPU核數小於2或實體記憶體小於2GB)或32位Windows機器上預設採用的GC方式 |
||
設定引數 |
通過引數-XX:+UseSerialGC指定 |
|||
並行 |
適用環境 |
適合於多CPU、對暫停時間要求較短的應用上。並行回收GC是server級別(CPU核數超過2且實體記憶體超過2GB)的機器(32位Windows機器除外)上預設採用的GC方式 |
||
設定引數 |
通過-XX:+UseParallelGC來指定 |
可通過-XX:+UseParallelGC或-XX:+UseParallelOldGC來強制指定 並行的執行緒數cpucore<=8 ? cpucore : 3+(cpucore*5)/8或通過-XX:ParallelGCThreads=<N>來強制挃定。 |
||
併發 |
適用環境 |
GC須配合舊生代使用CMS GC,CMS GC在進行舊生代GC時,有些過程是併發進行的 |
||
設定引數 |
在配置為使用CMS GC的情況下,通過-XX:+UseParNewGC來指定; |
-XX:+UseConcMarkSweepGC來啟用CMS進行舊生代物件的GC;併發的執行緒數預設為:( 並行GC執行緒數+3)/4,也可通過ParallelCMSThreads=<N>指定 |
-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled |
(四)、嘗試調優
根據第二章 分析gc資訊,我們知道gc調優5個目標就是:
1、 降低Full GC執行頻率
2、 降低Full GC消耗時間
3、 降低Full GC所造成的應用暫停時間
4、 降低Minor GC執行頻率
5、 降低Minor GC消耗時間
那麼如果做呢?
步驟:
1、 評估一下gc現狀,看它是否以及滿足期望值,比如gc頻率大於10秒,gc耗時小於500毫秒,暫停應用系統時間每次不到200毫秒,如果是這樣,那麼我們認為gc調優結束,不用走第二步了
2、 分析gc,根據gc調優目標,找出不符合目標的情況,使用不同方法除錯。
可能出現情況 |
分析與措施 |
|
降低Full GC執行頻率 |
Java RMI的定時GC觸發 |
程式呼叫了system.gc 通過:-XX:+DisableExplicitGC來禁止 |
Minor GC後總是有物件不斷的進入年老代,導致年老代不斷的滿 |
Survivor太小了系統響應太慢、請求量太大、每次請求分配記憶體多、分配的物件太大 1、擴大年老代大小(減少年輕代或調大heap);減少年輕代注意對minor gc的影響並且同時有可能造成fullgc還是嚴重;調大heap注意full gc的時間的延長,cpu要求夠多或者處理能力強。 2、程式優化(去掉一些不必要的快取) |
|
Minor GC後總是有物件不斷的進入年老代,而且這些進入年老代的物件在full時大部分都會被回收 |
1、降低Minor GC執行頻率(看下面) 2、讓物件儘量在Minor GC中就被回收掉:放大年輕代、放大survivor、增大TenuringThreshold但要注意這些有可能會造成minor gc執行頻繁 3、換CMS GC :年老代還沒滿就回收掉,從而降低Full GC觸發的可能 4、程式優化:提升響應速度、降低每次請求分配的記憶體 |
|
降低Full GC消耗時間 |
年老代太大了 |
1、改為並行GC,或者併發GC 2、減小Heap或舊生代吧 3、增加cpu |
降低Full GC所造成的應用暫停時間 |
修改gc策略 |
|
降低Minor GC執行頻率 |
每次請求分配的記憶體多、請求量大 |
1、 擴大heap、 2、 擴大年輕代代、擴大eden, 3、 橫向叢集部署應用程式 擴大時請綜合考慮;降低每次請求分配的記憶體; |
降低Minor GC消耗時間 |
1、新生代太大了 2、響應速度太慢了,導致每次Minor GC時存活的物件多 |
1、 減小點新生代吧; 2、 增加CPU吧或者升級CPU吧; |