環境
JDK8
準備工作
先準備好一個OOM程序:(程序是網上隨便找的)
import java.util.ArrayList;
public class TestOOM {
static class OOMObject {
private String content;
public OOMObject(String content){
this.content = content;
}
}
public static void main(String[] args) {
ArrayList<OOMObject> list = new ArrayList<>();
int i = 0;
while(true){
list.add(new OOMObject(i+++""));
}
}
}
我們來使用以下jvm命令啓動:-Xms2m -Xmx4m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\12340\Desktop
含義如下:
-Xms
設置JVM啓動時堆內存的初始化大小;-Xmx
設置堆內存最大值;-XX:+HeapDumpOnOutOfMemoryError
表示出現OOM異常時,生成堆快照hprof文件;-XX:HeapDumpPath=C:\Users\12340\Desktop
設置堆快照hprof文件存儲的位置。
這裏讀者可以根據自己的需要去調整。但請注意要放到有權限的文件夾,別放到需要管理員權限才能訪問的文件夾,不然Java沒有這個權限無法去操作。
這裏補充一個小tip:
如果你使用的是Intellij IDEA進行開發,可以很方便地在這裏修改JVM啓動參數:
點擊這裏的Edit Configurations
在VM options
欄裏填入我們的JVM參數即可。
最後運行我們的代碼,會顯示如下:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to C:\Users\12340\Desktop\java_pid14300.hprof …
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at TestOOM.main(TestOOM.java:17)
Heap dump file created [4755536 bytes in 0.030 secs]
此時已發生OOM並使進程異常退出。
並且通過Heap dump file created [4755536 bytes in 0.030 secs]
可以看到堆文件已經生成。
針對兩個問題,我們開始排查:
- OOM發生在哪個線程哪行代碼
- 是什麼實體類型過多導致OOM
排查
使用JVisualVM排查
JDK8提供了一個可視化工具Jvisualvm,在jdk的bin目錄下可以找到。
雙擊打開它:
點擊左上角的“裝入快照”按鈕。
把這裏的文件類型更改爲*.hporf
類型,然後選擇我們剛纔那個代碼導出的hprof文件,選擇打開。
我們可以看到在概述裏已經提示我們發生了OOM異常:
我們點擊紅色框裏的main
。
在這裏我們首先可以看到,是在代碼的第17行操作時發生了OOM:
那麼具體導致內存溢出的是什麼對象呢?
我們點開紅框下面的Java.util.ArrayList#9
看看:
在size這一欄可以看到,我們這個ArrayList
中存儲的元素個數達到了三萬多。那麼這些元素對象分別都是什麼呢?展開我們的elementData
看看:
我們可以發現全都是TestOOM
下的OOMObject
對象。那OOM是否真的就是因爲這個對象太多呢?到目前其實已經基本可以猜到了,但是還不能完全確認,我們可以再來看看類視圖。
在類視圖中,我們也可以發現OOMObject
對象確實很多,佔用了大量的堆內存。
總結可以得到:我們創建了太多無法回收的OOMObject
對象,所以導致了OOM。
總結
排查OOM問題時,總體可分爲如下幾步:
- 生成堆轉儲快照dump文件(或者稱hprof文件);
- 利用相關工具分析堆轉儲快照(本篇中使用的是JVisualVM可視化工具);
- 在線程快照(堆轉儲上的線程)中定位到發生OOM的線程和代碼位置;
- 綜合分析線程快照裏提示的本地變量信息及類視圖中的實例佔比,找出導致OOM的實體類型。
更多資料歡迎關注: