Java的dump檔案分析及JProfiler使用
1 dump檔案介紹
從軟體開發的角度上,dump檔案就是當程式産生異常時,用來記錄當時的程式狀态資訊(例如堆棧的狀态),用于程式開發定位問題。
idea配置發生OOM的時候指定路徑生成dump檔案
# 指定發生OOM異常的時候,在d盤下生成對應的dump檔案
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\
2 JProfiler介紹
2.1 下載下傳
JProfiler下載下傳:
連結:https://pan.baidu.com/s/1WXCc4FMOC3QQtjkhY4Qeow
提取碼:5xrm
版本:JProfiler 12.0.4
2.2 與idea內建
- 本地windows下載下傳并安裝好JProfiler
- idea安裝JProfiler插件
- 指定本地windows的JProfiler路徑[settings - tools]
4. 點選圖示啟動,JProfiler就預設監控到了指定Java程式
2.3 基本使用
①JProfiler基本參數
在概覽頁我們可以清晰的看到記憶體使用量、垃圾收集活動、類加載數量、線程個數和狀态、CPU 使用率等名額随時間變化的趨勢。
通過此圖,我們可以作出如下基本判斷:
- 程式在運作過程中會産生大量對象,但這些對象生命周期極短,大部分都能被垃圾收集器及時回收,不會造成記憶體無限增長。
- 加載類的數量在程式初始時增長較快,随後保持平穩,符合預期。
- 在程式運作過程中,有大量線程處于阻塞狀态,需要重點關注。
- 在程式剛啟動時,CPU 使用率較高,需要進一步探究其原因。
②測試分析dump檔案
- 模拟OOM
public class JProfilerTest {
public static void main(String[] args) throws InterruptedException {
List<byte[]> list = new ArrayList<>();
while (true) {
byte[] bytes = new byte[1024 * 1024 * 50];
list.add(bytes);
TimeUnit.SECONDS.sleep(1);
}
}
}
- 程式添加VM Options
# 監控OOM,發生OOM之後指定dump檔案生成到d:\
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\
- JProfiler方式啟動程式,觀察控制台列印
程式運作一段時間之後,發生OOM異常
5. 檢視dump檔案,用JProfiler打開
6. 分析dump檔案
檢視最大對象内部結構
可以發現是改list中含有了太多的byte[]數組
ps:
其他檢視方法類似
3 常見JVM問題
3.1 OOM
①堆溢出
原因:
1. 無法在Java堆中配置設定對象
2. 應用程式儲存了無法被GC回收的對象
3. 程式過度使用finalizer
排查思路:
- 檢視關鍵報錯資訊
- 使用記憶體映像分析工具(MAT或JProfiler)分析dump檔案,分析是記憶體洩漏還是記憶體溢出
- 如果是記憶體洩漏,通過工具檢視洩漏對象到GC Roots引用鍊,修複記憶體洩漏
- 如果不是,檢查代碼是否有死循環,遞歸等,再考慮用
-Xmx增加堆大小
demo代碼JVM配置參數:
- -Xms20m JVM初始配置設定的記憶體20m
- -Xmx20m JVM最大可用記憶體為20m
- -XX:+HeapDumpOnOutOfMemoryError 當JVM發生OOM時,自動生成DUMP檔案
- -XX:HeapDumpPath=/Users/mytest/Desktop/dump/ 生成DUMP檔案的路徑
②棧溢出
棧:虛拟機棧和本地方法棧,關于棧,Java虛拟機規範中描述了兩種異常:
:線程請求的棧深度大于虛拟機所允許的深度
StackOverflowError
:如果虛拟機棧可以動态擴充,當擴充時無法申請到足夠的記憶體時會抛出
OOM
原因:
1. 單個線程下,棧幀太大或虛拟機棧容量太小,記憶體無法配置設定
2. 不斷建立線程
排查思路:
- 檢視關鍵報錯資訊,确定是StackOverflow還是OOM
- 如果是StackOverflow,檢查代碼是否存在遞歸
- 如果是OOM,檢查是否有死循環建立線程或調用第三方接口建立線程,通過
降低每個線程棧大小
-Xss
③方法區溢出
方法區(又叫永久代,JDK8之後元空間替換了永久代),用于存放Class的相關資訊,如:類名、通路修飾符、常量池、字段描述、方法描述等。運作時産生大量的類,會填滿方法區,造成溢出。
溢出原因:
1. 使用CGLib生成大量代理類
2. 在Jdk7之前,頻繁錯誤的使用String.intern方法
3. 大量jsp和動态産生jsp
4. 應用長時間運作,沒有重新開機
排查思路:
- 檢查是否永久代空間設定的過小
-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
- 是否頻繁錯誤使用String.intern
- 是否與jsp有關
- 是否使用CGLib生成大量代理類
- 重新開機JVM
④直接記憶體溢出
直接記憶體不是虛拟機運作時資料區的一部分,也不是Java虛拟機規範中定義的記憶體區域。但是,這部分也被頻繁的使用,也可能導緻OOM。
原因:
1. 本機直接記憶體不受到Java堆大小限制,但是受到本機總記憶體大小限制
2. 直接記憶體由-XX:MaxDirectMemorySize指定,如果不指定,預設與Java堆最大值
一樣(-Xmx)
3. NIO程式中,使用ByteBuffer.allocateDirect(capability)配置設定的是直接内
存,可能導緻直接記憶體溢出
排查思路:
- 檢查代碼是否恰當
- 檢查JVM參數
-Xmx(java堆最大值),-XX:MaxDirectMemorySize是否合理
參考:https://zhuanlan.zhihu.com/p/95150243