'記一次有驚無險的 JVM 優化經歷'

"

背景

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

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

記一次有驚無險的 JVM 優化經歷


上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。

在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

6小時圖:

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

記一次有驚無險的 JVM 優化經歷


上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。

在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

6小時圖:

記一次有驚無險的 JVM 優化經歷


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

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

記一次有驚無險的 JVM 優化經歷


上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。

在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

6小時圖:

記一次有驚無險的 JVM 優化經歷


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

記一次有驚無險的 JVM 優化經歷


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

12小時

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

記一次有驚無險的 JVM 優化經歷


上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。

在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

6小時圖:

記一次有驚無險的 JVM 優化經歷


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

記一次有驚無險的 JVM 優化經歷


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

12小時

記一次有驚無險的 JVM 優化經歷


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

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

記一次有驚無險的 JVM 優化經歷


上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。

在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

6小時圖:

記一次有驚無險的 JVM 優化經歷


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

記一次有驚無險的 JVM 優化經歷


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

12小時

記一次有驚無險的 JVM 優化經歷


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

記一次有驚無險的 JVM 優化經歷


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

GC日誌

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

"

背景

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

記一次有驚無險的 JVM 優化經歷

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

有一次對系統進行例行檢查,使用PinPoint查看下服務器”Heap Usage”的使用情況時,發現在有一個系統Full GC非常頻繁,大約五分鐘一次Full GC,嚇我一跳。

這麼頻繁的Full GC,導致系統暫停處理業務,對系統的實時可用性大打折扣。

我檢查了一下Tomcat(Tomcat8.5.28)配置,發現在tomcat沒有作任何關於JVM內存的設置,全部使用默認模式。

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

GC數據

在業務峰期間,通過PinPoint觀察的A、B節點的”Heap Usage”使用情況,分別進行以下幾個時間段數據。

3小時圖:

記一次有驚無險的 JVM 優化經歷


上圖B系統在三個小時內,一共發生了22次Full GC,大約每8分鐘進行一次Full GC。

每次Full GC的時間大概有150ms左右,即B系統在三個小時內,大約有3300ms暫停系統運行。

從上圖來看,堆的空間最大值在890M左右,但在堆空間的大小大約200M就發生Full GC了,從系統資源的利用角度來考慮,這個使用率太低了。

記一次有驚無險的 JVM 優化經歷


上圖A系統在3個小時內,一共發生了0次Full GC,嗯,就是沒有任何停頓。

在這3小時,系統一直在處理業務,沒有停頓。堆的總空間大約1536m,目前堆的空間大於500M。

6小時圖:

記一次有驚無險的 JVM 優化經歷


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

記一次有驚無險的 JVM 優化經歷


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

12小時

記一次有驚無險的 JVM 優化經歷


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

記一次有驚無險的 JVM 優化經歷


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

GC日誌

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

記一次有驚無險的 JVM 優化經歷

上圖全部是” [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是因為老年代對象佔用的空間的大小已經超過老年代容量 引發的Full GC。

[ParOldGen: 89513K->69918K(89600K)]

究其原因,是因為分配給老年代的空間太小,遠遠不能滿足系統對業務的需要。

這導致老年代的空間常常被佔滿,老年代的空間滿了,導致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收集器中,堆的空間不是越大越好。


分析與優化

總體分析:

  • B系統的Full GC過於頻繁,是因為老生代只有約108M空間,根本無法滿足系統在高峰時期的內存空間需求
  • 由於ParOldGen(老年代)常常被耗盡,所以就發生Full GC事件了
  • A系統的堆初始空間(Xms)和堆的最大值(Xmx)均為1536m,完全可以滿足業務高峰期的內存需求

優化策略:

  • B系統先增加堆空間大小,即通過設置Xms、 Xmx值增加堆空間。直接把Xms和Xmx均設置為1024M。
  • 堆的啟動空間(Xms)直接設置為堆的最大值的原因是:因為直接把Xms設置為最大值(Xmx)可以避免JVM運行時不停的進行申請內存,而是直接在系統啟動時就分配好了,從而提高系統的效率。
  • 把Xms(堆大小)設置為1024M,是因為採用JDK的建議,該建議通過命令得到:
  • java -XX:+PrintCommandLineFlags -version
  • 綜合下來的B系統的JVM參數設置如下:
  • export JAVA_OPTS="-server –Xms1024m -Xmx1024m -XX:+UseParallelOldGC -verbose:gc -Xloggc:../logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
  • A系統JVM參數設置保持不變,以便觀察系統運行情況,即:
  • export JAVA_OPTS="-server -Xms1536m -Xmx1536m -XX:+UseParallelOldGC -verbose:gc -Xloggc:../logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps


  • 將A、B節點系統的JVM參數採用2套參數,是為了驗證A或B的參數更適合實際情況。
"

相關推薦

推薦中...