1. 程式人生 > >JVM引數設定、分析

JVM引數設定、分析

不管是YGC還是Full GC,GC過程中都會對導致程式執行中中斷,正確的選擇不同的GC策略,調整JVM、GC的引數,可以極大的減少由於GC工作,而導致的程式執行中斷方面的問題,進而適當的提高Java程式的工作效率。但是調整GC是以個極為複雜的過程,由於各個程式具備不同的特點,如:web和GUI程式就有很大區別(Web可以適當的停頓,但GUI停頓是客戶無法接受的),而且由於跑在各個機器上的配置不同(主要cup個數,記憶體不同),所以使用的GC種類也會不同(如何選擇見GC種類及如何選擇)。本文將注重介紹JVM、GC的一些重要引數的設定來提高系統的效能。

JVM引數的含義
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
並行收集器相關引數

在這裡插入圖片描述
CMS相關引數

在這裡插入圖片描述
輔助資訊

在這裡插入圖片描述
在這裡插入圖片描述

GC效能方面的考慮

對於GC的效能主要有2個方面的指標:吞吐量throughput(工作時間不算gc的時間佔總的時間比)和暫停pause(gc發生時app對外顯示的無法響應)。

  1. Total Heap
    預設情況下,vm會增加/減少heap大小以維持free space在整個vm中佔的比例,這個比例由MinHeapFreeRatio和MaxHeapFreeRatio指定。 一般而言,server端的app會有以下規則:
    對vm分配儘可能多的memory;
    將Xms和Xmx設為一樣的值。如果虛擬機器啟動時設定使用的記憶體比較小,這個時候又需要初始化很多物件,虛擬機器就必須重複地增加記憶體。
    處理器核數增加,記憶體也跟著增大。
  2. The Young Generation
    另外一個對於app流暢性執行影響的因素是young generation的大小。young generation越大,minor collection越少;但是在固定heap size情況下,更大的young generation就意味著小的tenured generation,就意味著更多的major collection(major collection會引發minor collection)。
    NewRatio反映的是young和tenured generation的大小比例。NewSize和MaxNewSize反映的是young generation大小的下限和上限,將這兩個值設為一樣就固定了young generation的大小(同Xms和Xmx設為一樣)。
    如果希望,SurvivorRatio也可以優化survivor的大小,不過這對於效能的影響不是很大。SurvivorRatio是eden和survior大小比例。
    一般而言,server端的app會有以下規則:
    首先決定能分配給vm的最大的heap size,然後設定最佳的young generation的大小; 如果heap size固定後,增加young generation的大小意味著減小tenured generation大小。
    讓tenured generation在任何時候夠大,能夠容納所有live的data(留10%-20%的空餘)。

經驗&&規則

  1. 年輕代大小選擇
    響應時間優先的應用:儘可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇).在此種情況下,年輕代收集發生的頻率也是最小的.同時,減少到達年老代的物件.
    吞吐量優先的應用:儘可能的設定大,可能到達Gbit的程度.因為對響應時間沒有要求,垃圾收集可以並行進行,一般適合8CPU以上的應用.
    避免設定過小.當新生代設定過小時會導致:1.YGC次數更加頻繁 2.可能導致YGC物件直接進入舊生代,如果此時舊生代滿了,會觸發FGC.

  2. 年老代大小選擇
    響應時間優先的應用:年老代使用併發收集器,所以其大小需要小心設定,一般要考慮併發會話率和會話持續時間等一些引數.如果堆設定小了,可以會造成記憶體碎 片,高回收頻率以及應用暫停而使用傳統的標記清除方式;如果堆大了,則需要較長的收集時間.最優化的方案,一般需要參考以下資料獲得:併發垃圾收集資訊、持久代併發收集次數、傳統GC資訊、花在年輕代和年老代回收上的時間比例。
    吞吐量優先的應用:一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代.原因是,這樣可以儘可能回收掉大部分短期物件,減少中期的物件,而年老代盡存放長期存活物件.

  3. 較小堆引起的碎片問題
    因為年老代的併發收集器使用標記,清除演算法,所以不會對堆進行壓縮.當收集器回收時,他會把相鄰的空間進行合併,這樣可以分配給較大的物件.但是,當堆空間較小時,執行一段時間以後,就會出現"碎片",如果併發收集器找不到足夠的空間,那麼併發收集器將會停止,然後使用傳統的標記,清除方式進行回收.如果出現"碎片",可能需要進行如下配置:
    -XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啟對年老代的壓縮.
    -XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這裡設定多少次Full GC後,對年老代進行壓縮

  4. 用64位作業系統,Linux下64位的jdk比32位jdk要慢一些,但是吃得記憶體更多,吞吐量更大

  5. XMX和XMS設定一樣大,MaxPermSize和MinPermSize設定一樣大,這樣可以減輕伸縮堆大小帶來的壓力

  6. 使用CMS的好處是用盡量少的新生代,經驗值是128M-256M, 然後老生代利用CMS並行收集, 這樣能保證系統低延遲的吞吐效率。 實際上cms的收集停頓時間非常的短,2G的記憶體, 大約20-80ms的應用程式停頓時間

  7. 系統停頓的時候可能是GC的問題也可能是程式的問題,多用jmap和jstack檢視,或者killall -3 java,然後檢視java控制檯日誌,能看出很多問題。(相關工具的使用方法將在後面的blog中介紹)

  8. 仔細瞭解自己的應用,如果用了快取,那麼年老代應該大一些,快取的HashMap不應該無限制長,建議採用LRU演算法的Map做快取,LRUMap的最大長度也要根據實際情況設定。

  9. 採用併發回收時,年輕代小一點,年老代要大,因為年老大用的是併發回收,即使時間長點也不會影響其他程式繼續執行,網站不會停頓

  10. JVM引數的設定(特別是 –Xmx –Xms –Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold等引數的設定沒有一個固定的公式,需要根據PV old區實際資料 YGC次數等多方面來衡量。為了避免promotion faild可能會導致xmn設定偏小,也意味著YGC的次數會增多,處理併發訪問的能力下降等問題。每個引數的調整都需要經過詳細的效能測試,才能找到特定應用的最佳配置。

promotion failed:

垃圾回收時promotion failed是個很頭痛的問題,一般可能是兩種原因產生,第一個原因是救助空間不夠,救助空間裡的物件還不應該被移動到年老代,但年輕代又有很多物件需要放入救助空間;第二個原因是年老代沒有足夠的空間接納來自年輕代的物件;這兩種情況都會轉向Full GC,網站停頓時間較長。

解決方方案一:

第一個原因我的最終解決辦法是去掉救助空間,設定-XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0即可,第二個原因我的解決辦法是設定CMSInitiatingOccupancyFraction為某個值(假設70),這樣年老代空間到70%時就開始執行CMS,年老代有足夠的空間接納來自年輕代的物件。

解決方案一的改進方案:

又有改進了,上面方法不太好,因為沒有用到救助空間,所以年老代容易滿,CMS執行會比較頻繁。我改善了一下,還是用救助空間,但是把救助空間加大,這樣也不會有promotion failed。具體操作上,32位Linux和64位Linux好像不一樣,64位系統似乎只要配置MaxTenuringThreshold引數,CMS還是有暫停。為了解決暫停問題和promotion failed問題,最後我設定-XX:SurvivorRatio=1 ,並把MaxTenuringThreshold去掉,這樣即沒有暫停又不會有promotoin failed,而且更重要的是,年老代和永久代上升非常慢(因為好多物件到不了年老代就被回收了),所以CMS執行頻率非常低,好幾個小時才執行一次,這樣,伺服器都不用重啟了。

-Xmx4000M -Xms4000M -Xmn600M -XX:PermSize=500M -XX:MaxPermSize=500M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log 

CMSInitiatingOccupancyFraction值與Xmn的關係公式

上面介紹了promontion faild產生的原因是EDEN空間不足的情況下將EDEN與From survivor中的存活物件存入To survivor區時,To survivor區的空間不足,再次晉升到old gen區,而old gen區記憶體也不夠的情況下產生了promontion faild從而導致full gc.那可以推斷出:eden+from survivor < old gen區剩餘記憶體時,不會出現promontion faild的情況,即:
(Xmx-Xmn)*(1-CMSInitiatingOccupancyFraction/100)>=(Xmn-Xmn/(SurvivorRatior+2)) 進而推斷出: CMSInitiatingOccupancyFraction <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100

例如:
當xmx=128 xmn=36 SurvivorRatior=1時 CMSInitiatingOccupancyFraction<=((128.0-36)-(36-36/(1+2)))/(128-36)*100 =73.913
當xmx=128 xmn=24 SurvivorRatior=1時 CMSInitiatingOccupancyFraction<=((128.0-24)-(24-24/(1+2)))/(128-24)*100=84.615…
當xmx=3000 xmn=600 SurvivorRatior=1時 CMSInitiatingOccupancyFraction<=((3000.0-600)-(600-600/(1+2)))/(3000-600)*100=83.33
CMSInitiatingOccupancyFraction低於70% 需要調整xmn或SurvivorRatior值。