EDAS-如何快速定位OOM問題【雲享團】
什麼是OOM?
相信很多“程序猿”都能知道,OOM 異常,就是我們常見的: “java.lang.OutOfMemoryError” 在應用開發中,是比較常見的一種異常,主要分為三種:
1. OutOfMemoryError: PermGen space
2. OutOfMemoryError: Java heap space
3. OutOfMemoryError:unable to create new native thread
OOM的危害
出現OOM的情況,往往會導致:
1、應用服務異常
2、線程異常
3、程序崩潰
以及其他未知的問題,相信看到這幾點就能體會到 “ OOM ” 的“ 恐怖 ”了。
如何排查OOM問題
在遇到這OOM問題的時候,要如何分析原因,是直接去死磕代碼?還是去調整工程架構?我相信這樣的方式很多時候都隻會徒勞,那針對這類問題,我們可以如何快速的去排查定位呢?
模擬異常場景
為了說清楚整個排查過程,我們編寫一段OutOfMemoryError測試代碼,用來模擬出 oom 場景。
servlet 測試代碼:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("heap test begin");
List<TestCase> cases = new ArrayList<TestCase>();
while(true){
cases.add(new TestCase());
}
//super.doGet(req, resp);
}
public class TestCase {
public int id;
public String name;
public String[] array = new String[1024];
public List<String> list = new ArrayList<>(1024);
web.xml映射路徑:
<servlet>
<servlet-name>Heaptest</servlet-name>
<servlet-class>com.alibaba.edas.tradeshop.threadtest.HeapOutOfMemory</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Heaptest</servlet-name>
<url-pattern>/heap.htm</url-pattern>
</servlet-mapping>
獲取dump文件
Dump文件是進程的內存鏡像,可以把程序的執行狀態通過調試器保存到dump文件中,是開發人員定位jvm問題的“利器”。
方式一:edas控製台部署添加參數
通過edas控製台部署啟動應用,可以指定jvm參數,在控製台上指定最大Heap Size 和初始化Heap Size 都為100m (注意現在配置的值隻是為了測試),
另外還可以自定義加上一些參數,例如:
-XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError XX:HeapDumpPath=/home/admin/dump/
控製台上設置:
PrintGCDetails 會將gc日誌打印出來,XX:HeapDumpPath指定dump文件存儲路徑,當出現OOM問題時,會在/home/admin/dump/生成一個dump文件
方式二:通過jmap獲取
jmap 是 jdk自帶的一個 jvm 檢測工具,為了能讓通過jmap方式的時候獲取到堆棧溢出的dump文件,我們將代碼改為:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("heap test begin");
List<TestCase> cases = new ArrayList<TestCase>();
while(true){
try{
cases.add(new TestCase());
}catch(Throwable t){
}
}
//super.doGet(req, resp);
}
訪問到測試代碼入口,在應用服務器上執行:
jmap -dump:live,format=b,file=heap.bin <pid>
可以獲取到heap.bin文件。
測試堆棧溢出
admin用戶登錄到服務器上,找到tomcat進程ID,執行 ./jstat -gcutil {pid} {時間間隔} ,觀察 gc 執行情況:
可以看到當前YGC 和 FGC 都不頻繁,屬於正常狀態。
當我們訪問測試路徑: https://{ip}:port/path/heap.htm
執行結束後,可以發現,eden 區和 old 區 都瞬間打滿,而且短時間內發生多次 FGC:
MAT 內存分析
mat 是一個快速分析 java 內存的工具,mat 官網
到服務器上,之前我們指定的路徑下麵,方式一:/home/admin/dump/ 可以找到生成的 hprof 文件,方式二:生成的heap.bin文件,下載下來後,用 mat 內存分析軟件打開
經過mat 分析後,馬上可以得出一份分析報告,從這可以很清晰的定位到,是由於 “ HeapOutOfMemory.doGet ” 這段代碼入口導致的堆棧溢出,終於,我們的“罪魁禍首”付出水麵了,原來,是由於代碼中出現了一段死循環一直在新建class 類,導致堆棧溢出,接下來,該知道怎麼去修改代碼了吧。
小結
當遇到jvm問題的時候,不必驚慌,也不要因為是技術底層問題而感到無從下手,我們有很多種方法可以方便定位到原因,除了本文中提到的 mat 內存分析工具,我們還有很多其他的小工具可以利用起來,jdk 本身就提供了很多這樣的支持工具,例如: jconsole、jmap、jstat、jinfo、jvisualvm 等等,會利用好這些小工具,下次再遇到類似的問題後,就可以得心應手了。
最後更新:2017-04-14 15:00:30