概述
JVM OutOfMemoryError(OOME,內存溢出錯誤)導致宕機的情況有多種,此處主要以超出堆最大值為例來說明OOME問題排查過程。
通過設置JVM相關參數可以在發生OOME時生成堆內存的快照,以便后期分析問題。
JVM 主要參數
Debug 時用到的JVM參數有(具體值可以自己按需配置):
# 指定堆內存最大值限制為10MB
-Xmx10M
# 指定堆快照生成路徑為 /test/dump,默認文件名為:java_pidpid.hprof
-XX:HeapDumpPath=/test/dump
# 指定在發生OOME時生成堆快照文件
-XX:+HeapDumpOnOutOfMemoryError
OOME 宕機實驗
OOME 實驗代碼
package com.dotions;
import java.util.ArrayList;
import java.util.List;
public class OOMETest {
public static void main(String[] args) {
List<Long> list = new ArrayList<Long>();
// 通過死循環迫使內存溢出
while(true) {
list.add(new Long(0));
}
}
}
JVM 運行參數
-Xmx10M
-XX:HeapDumpPath=C:/oome_test_dump.hprof
-XX:+HeapDumpOnOutOfMemoryError
- 堆最大內存為10MB;
- 堆快照文件生成路徑為:C:/,文件名為:oome_test_dump.hprof
- 啟用在發生OOME時生成堆快照的配置開關;
Eclipse 中的運行結果
生成的Dump文件
Dump 文件查看
(一)使用 jvisualvm 查看
jvisualvm.exe 是JDK自帶的工具,可以對堆文件進行查看與簡單的分析,參考文檔:http://visualvm.java.net/zh_CN/gettingstarted.html。
jvisualvm.exe 在 ** {JAVA_HOME}/bin/** 目錄下,如下圖:
jvisualvm 界面
Dump 文件載入到 jvisualvm
在菜單欄中點擊 【文件】==> 【載入】,然后看到如下對話框:
先將文件類型選擇為:堆 Dump (.hprof, .)* ,再找之前OOME生成的dump文件,最后點擊【打開】按鈕進行載入。
Dump 文件——概要信息
此處可以發現導致OOME的線程是main線程;
最大的對象是ArrayList類的實例。
Dump 文件——類信息
在此處可以發現Long類型的實例對象占了整個堆實例的97%以上;
Dump 文件——實例數
Dump文件——OQL控制臺查詢展示
(二)使用 jhat 命令查看
jhat (Java heap analyzes toolkit)是 JDK 自帶的 Java 堆內容分析工具。可以通過 jhat 命令加載 dump 文件并啟動 HTTP 服務來瀏覽 dump 文件中的內容。
**jhat **命令介紹:
jhat 啟動HTTP服務并加載指定dump文件:
# 指定 dump 文件路徑為:
# jhat 命令默認端口為7000,此處指定 HTTP 服務端口為:80
jhat -port 80 c:/oome_test_dump.hprof
命令執行結果:
此時HTTP服務已啟動成功,可以在瀏覽器中輸入地址:
http://localhost/
對 dump 文件進行瀏覽,首頁展示如下:
對 dump 文件中實例數統計展示:
實驗總結
該實驗中我們可以發現,由于代碼執行時配置的JVM參數的限制,使得最大堆內存空間為 10MB。
而程序是死循環,且list 對象沒有釋放,導致JVM 在GC后無法釋放堆內存空間,從而超過了堆內存最大值的限制,導致了OOME。
在實際開發和部署是,根據實際情況來設置、調整堆內存空間限制。
以Eclipse 運行狀態為例,如下圖:
** 生產環境,可以對JVM 中的各個分區分別設置對應的空間閾值。此時 OOME 問題分析就必須對應到具體的分區上,而不是只看堆內存空間的最大值。**