閱讀897 返回首頁    go 阿裏雲 go 技術社區[雲棲]


EDAS-如何快速定位OOM問題【雲享團】

2e216c041cc68ffaec76f55e7116c3be50fd6866

什麼是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/

控製台上設置:

screenshot.png

screenshot.png

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 執行情況:

screenshot.png

可以看到當前YGC 和 FGC 都不頻繁,屬於正常狀態。

當我們訪問測試路徑: https://{ip}:port/path/heap.htm

執行結束後,可以發現,eden 區和 old 區 都瞬間打滿,而且短時間內發生多次 FGC:

screenshot.png

MAT 內存分析

mat 是一個快速分析 java 內存的工具,mat 官網
到服務器上,之前我們指定的路徑下麵,方式一:/home/admin/dump/ 可以找到生成的 hprof 文件,方式二:生成的heap.bin文件,下載下來後,用 mat 內存分析軟件打開

screenshot.png

screenshot.png

經過mat 分析後,馬上可以得出一份分析報告,從這可以很清晰的定位到,是由於 “ HeapOutOfMemory.doGet ” 這段代碼入口導致的堆棧溢出,終於,我們的“罪魁禍首”付出水麵了,原來,是由於代碼中出現了一段死循環一直在新建class 類,導致堆棧溢出,接下來,該知道怎麼去修改代碼了吧。

小結

當遇到jvm問題的時候,不必驚慌,也不要因為是技術底層問題而感到無從下手,我們有很多種方法可以方便定位到原因,除了本文中提到的 mat 內存分析工具,我們還有很多其他的小工具可以利用起來,jdk 本身就提供了很多這樣的支持工具,例如: jconsole、jmap、jstat、jinfo、jvisualvm 等等,會利用好這些小工具,下次再遇到類似的問題後,就可以得心應手了。

關注
0人關注該文章

評論文章 (0)

最後更新:2017-04-14 15:00:30

  上一篇:go 正則表達式全部符號解釋
  下一篇:go DB2數據庫安全(二)——身份認證