天天看點

JVM 調優案例分析

JVM 調優案例分析

一、腳本引擎導緻metaspace fullgc及記憶體溢出

現象:系統日常運作正常突然發生fullgc甚至記憶體溢出,重新開機後恢複正常但是過了幾天又會突然發生頻繁fullgc觸發告警,告警資訊如下:

JVM 調優案例分析

首先發生fullgc,我們需要定位fullgc發生在jvm哪個區域?我們可以通過cat也可以通過gc日志查詢發生的區域,目前有兩種方式可以快速定位到gc區域:

1、Cat的Problem中可以直接查詢error的詳細資訊(時間比較久了找不到對應的cat截圖了)

2、通過gclog查詢(大部分系統沒有添加gc日志列印),常見配置如下

# 必備
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintTenuringDistribution
-XX:+PrintHeapAtGC
-XX:+PrintReferenceGC
-XX:+PrintGCApplicationStoppedTime
  
# 可選
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1
  
# GC日志輸出的檔案路徑
-Xloggc:/path/to/gc-%t.log
# 開啟日志檔案分割
-XX:+UseGCLogFileRotation
# 最多分割幾個檔案,超過之後從頭檔案開始寫
-XX:NumberOfGCLogFiles=14
# 每個檔案上限大小,超過就觸發分割
-XX:GCLogFileSize=100M      
JVM 調優案例分析

通過以上分析可以看出fullgc發生再metaspace區,按照以往的經驗,Metaspace在系統穩定運作一段時間後占用空間應該比較穩定才對。我們知道Metaspace主要存儲類的中繼資料,比如我們加載了一個類,出現頻繁fullgc的時候說明在不斷的加載類,194621k->165280k發現也解除安裝了不少類,說明在頻繁地生成大量”一次性“的類,于是分析dump檔案:

具體步驟:

1.申請目前機器堡壘機權限

2.登入堡壘機

3.檢視目前Java程序 ​​

​ps -ef|grep java​

​​ 4.将此機器下線(防止進行dump檔案時對其他業務造成影響)

5.進行應用dump操作

sudo -u tuhu jmap -dump:format=b,file=/tmp/heapDump.hprof [pid]

6.修改dump檔案權限 ​

​sudo chmod 644 /tmp/heapDump.hprof​

​ 7.對dump檔案進行壓縮操作(dump的檔案很大 一般不壓縮的話 很難下載下傳的)

cd /tmp 進入到 /tmp 目錄

​tar -zcvf dump.tar.gz ./heapDump.hprof​

​ 進行壓縮,得到壓縮檔案 dump.tar.gz

8.在檔案視圖裡面 下載下傳dump檔案

9.下載下傳到本地解壓: ​

​tar -xzvf dump.tar.gz​

​ //解壓tar.gz
JVM 調優案例分析

通過visualvm分析(mat沒有發現問題)發現頻繁建立了很多Script類,看reference是Lhs類,于是分析代碼Lhs的compile方法:

/**
 * 使用aviator編譯原始表達式
 */
public void compile() {
    RuleAssert.requireNotEmptyWhenCompiling(expression,
            RuleErrorEnum.RULE_COMPILE_INVALID_EXPRESSION);
    compiledExpression = AviatorEvaluator.compile(this.expression);
}      

經過查詢aviator官網文檔分析發現确實存在記憶體溢出的問題,在編譯表達式的時候每次都産生新的匿名類,這些類會占用 JVM 方法區(Perm 或者 metaspace)

JVM 調優案例分析

解決方案:問題修複比較簡單編譯的時候使用緩存模式,如:AviatorEvaluator.compile(this.expression, true),重新釋出後metaspace穩定

二、kafka消費不均衡導緻cpu負載高

現象:廣告投放服務突然出現很多cpu負載過高告警,尤其是在釋出的時候告警頻繁,告警資訊如下:

JVM 調優案例分析
JVM 調優案例分析

出現負載過高首先我們要明白什麼場景下回導緻cpu負載過高?才能快速定位到問題原因,個人總結cpu占用高主要有以下幾點:

1、存線上程無限循環的占用cpu

2、Young GC頻繁導緻CPU占用率飙升

3、建立了大量線程的應用程式導緻cpu飙升

4、存在密集型計算線程

這裡推薦一個比較好用的工具 arthas(阿爾薩斯),提供了很多指令如:thread,jvm,memory等,于是我們通過thread分析線程情況:

JVM 調優案例分析

通過線程資訊可以快速定位到是mkt_ta_track_topic.sub消費線程占用了大量的cpu資源,通過簡單的分析定位到問題,主要因為兩個問題:

1、ta埋點(消息量很大)測試目前是做了簡單的判斷列印日志,沒有做其他業務邏輯并且使用了多線程消費,消費速度很快相當于空循環

2、mkt_ta_track_topic當時是申請了8個分區,目前隻有5台機器意味着有部分機器可能消費多個分區,此時占用cpu更嚴重;另外服務釋出的時候也會導緻kafka分區重配置設定也會出現上面說的負載過高問題

解決方案:

1、調整線程池多線程消費為單線程消費(後面做業務正常處理再做對應的調整)

2、申請擴充機器添加3台服務,為什麼添加機器不是調整分區?服務負載本來就很高,借此機會擴容,擴容之後資源使用率依然有75.97%

三:FullGC 停頓時間過長導緻上遊調用服務逾時

背景:新接手了一個服務,上遊回報偶爾調用逾時,和上遊确認逾時發生時間點和具體逾時時間,(逾時時間為 1000 ms)。

檢視當時系統運作情況,發現正在發生 FullGC ,且停頓時間超過 1000 ms。

JVM 調優案例分析

要對 FullGC 停頓時間太長調優,我們就得先知道影響 FullGc 停頓時間太長的原因有哪些,通常來說,有以下幾種:

  1. 堆記憶體過大
  2. 使用了非低延遲的垃圾收集器(GC)
  3. FGC 期間系統在做其他操作,如 swap 交換空間
  4. 堆記憶體碎片化嚴重
  5. 顯示System.gc調用

經排查發現屬于第二種情況,JDK 使用為 jdk8 ,預設垃圾收集器為 Parallel Scanenge 收集器,該收集器主打吞吐量,不關心停頓時間。想要低延遲,我們應該使用 CMS 收集器。CMS 是一款以擷取最短回收停頓時間為目标的收集器。

解決方案:

使用 CMS: