一次生產的JVM優化

背景

    生產環境有二臺阿里雲服務器,均爲同一時期購買的,CPU、內存、硬盤等配置相同。具體配置以下:java

   

        節點tomcat

CPU服務器

內存優化

硬盤阿里雲

其它spa

A3d

2CPU            日誌

4Gserver

普通雲盤對象

Centos6.4 64位+JDK1.8.0_121

B

2CPU            

4G

普通雲盤

Centos6.4 64位+JDK1.8.0_121

    因爲這二服務器硬件和軟件配置相同,而且運行相同的程序,因此在Nginx輪詢策略均weight=1,即平臺的某個流量由這二臺機器平分。

    有一次對系統進行例行檢查,使用PinPoint查看下服務器」Heap Usage」的使用狀況時,發現,在有一個系統Full GC很是頻繁,大約五分鐘一次Full GC(若是不明白Full GC的什麼意思的,請自行百度),嚇我一跳。這麼頻繁的Full GC,致使系統暫停處理業務,對系統的實時可用性大打折扣。我檢查了一下Tomcat(Tomcat8.5.28)配置,發如今tomcat沒有做任何關於JVM內存的設置,所有使用默認模式。因爲這二服務器硬件和軟件配置相同,而且運行相同的程序,因此在Nginx輪詢策略均weight=1,即平臺的某個流量由這二臺機器平分。

GC數據

    在業務峯期間,經過PinPoint觀察的A、B節點的」Heap Usage」使用狀況,分別進行如下幾個時間段數據。

    3小時圖:

    

    上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率過低了。

     

    上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。 在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

    6小時圖:

    

    上圖B系統在6個小時的數據統計和3個小時很像,6個小時內一共發生了N次Full GC,均是堆的空間小於200M就發生Full GC了。

    

    上圖A系統在6個小時內,一共發生了0次Full GC,表現優秀。

 12小時

    

    上圖B系統在12個小時內,一共發生了N次Full GC,左邊Full GC比較少,是由於咱們的業務主要集中白天,雖然晚上屬於非業務高峯期間,仍是有Full GC。

    

    上圖A系統在12個小時內,一共發生了0次Full GC,表現優秀。

GC日誌

    看下gc.log文件,由於咱們兩臺服務器都輸出了gc的詳細日誌,先看下B系統的Full GC日誌。

    

    

    上圖所有是」 [Full GC (Ergonomics)」日誌,是由於已經去掉」 GC (Allocation Failure」日誌,這樣更方便觀察和分析日誌,選取GC日誌文件最後一條Full GC日誌。

    2018-12-24T15:52:11.402+0800: 447817.937: [Full GC (Ergonomics) [PSYoungGen: 480K->0K(20992K)] [ParOldGen: 89513K->69918K(89600K)] 89993K->69918K(110592K), [Metaspace: 50147K->50147K(1095680K)], 0.1519366 secs] [Times: user=0.21 sys=0.00, real=0.15 secs]

    能夠計算獲得如下信息:

    堆的大小:110592K=108M

    老生代大小:89600K=87.5M

    新生代大小:20992K=20.5M

    分析:此次Full GC是由於老年代對象佔用的空間的大小已經超過老年代容量 ([ParOldGen: 89513K->69918K(89600K)])引起的Full GC。是由於分配給老年代的空間過小,遠遠不能知足系統對業務的須要,致使老年代的空間經常被佔滿,老年代的空間滿了,致使的Full GC。因爲老年代的空間比較小,因此每次Full GC的時間也比較短。

    A系統日誌,只有2次Full GC,這2次GC均發生在系統啓動時:

    

    7.765: [Full GC (Metadata GC Threshold) [PSYoungGen: 18010K->0K(458752K)] [ParOldGen: 15142K->25311K(1048576K)] 33153K->25311K(1507328K), [Metaspace: 34084K->34084K(1081344K)], 0.0843090 secs] [Times: user=0.14 sys=0.00, real=0.08 secs]

    能夠獲得如下信息:

    堆的大小:1507328K=1472M

    老生代大小:89600K=1024M

    新生代大小:20992K=448M

    分析:A系統只有系統啓動纔出現二次Full GC現象,並且是」 Metadata GC Threshold」引發的,而不是堆空間引發的Full GC。雖然通過一個星期的觀察,A系統沒有Full GC,但一旦發生Full GC時間則會比較長。其它系統增長髮現過,1024M的老年代,Full GC持續的時間大約是90ms秒。因此看得出來推也不是越大越好,或者說在UseParallelOldGC收集器中,堆的空間不是越大越好。

分析與優化

整體分析:

  1. B系統的Full GC過於頻繁,是由於老生代只有約108M空間,根本沒法知足系統在高峯時期的內存空間需求。因爲ParOldGen(老年代)經常被耗盡,因此就發生Full GC事件了。
  2. A系統的堆初始空間(Xms)和堆的最大值(Xmx)均爲1536m,徹底能夠知足業務高峯期的內存需求。

優化策略:

  1. B系統先增長堆空間大小,即經過設置Xms、 Xmx值增長堆空間。直接把Xms和Xmx均設置爲1024M。直接堆的啓動空間(Xms)直接設置爲堆的最大值的緣由是:由於直接把Xms設置爲最大值(Xmx)能夠避免JVM運行時不停的進行申請內存,而是直接在系統啓動時就分配好了,從而提升系統的效率。把Xms(堆大小)設置爲1024M,是由於採用JDK的建議,該建議經過命令獲得」 java -XX:+PrintCommandLineFlags -version」 。其中,「-XX:MaxHeapSize=1004719104」,即Xmx爲1024M,其它建議暫時不採納。因此綜合下來的B系統的JVM參數設置以下:export JAVA_OPTS="-server –Xms1024m -Xmx1024m -XX:+UseParallelOldGC  -verbose:gc -Xloggc:../logs/gc.log  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
  2. A系統JVM參數設置保持不變,以便觀察系統運行狀況,即:export JAVA_OPTS="-server -Xms1536m -Xmx1536m -XX:+UseParallelOldGC  -verbose:gc -Xloggc:../logs/gc.log  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
  1. 將A、B節點系統的JVM參數採用2套參數,是爲了驗證A或B的參數更適合實際狀況。
相關文章
相關標籤/搜索