java OOM問題排查

在做服務器端開發的時候,經常會遇到服務由於內存溢出掛掉的情況,這種情況的發生一般來說是很難預期的,也比較難以重現,對於這種問題,一般可以通過記錄內存溢出時候的堆信息來排查。


1、首先可以查看服務器運行日誌以及項目記錄的日誌,捕捉到內存溢出異常。


2、如果程序掛掉了,但是沒有找到任何這個操作的日誌記錄。這時查看一下/var/log/messages文件。messages 日誌是核心系統日誌文件。它包含了系統啓動時的引導消息,以及系統運行時的其他狀態消息。在messages裏會出現以下信息:

圖1

造成這種情況的原因是因爲,服務器以及項目日誌都只能記錄JVM內發生的內存溢出,也就是說heap(堆)的大小超出了JVM設置的大小。然而如果JVM設置的堆大小超出了操作系統允許的內存大小,那麼操作系統會直接殺死進程,這種情況JVM就無法記錄本次操作。

從圖1中可以看出操作系統由於內存使用率過高,直接殺死了評分最高的進程,這裏是java進程。Linux對於每個進程有一個OOM評分,這個評分在/proc/pid/oom_score文件中。例如/proc/8398/oom_score,如果不希望殺死這個進程,就將oom_adj內容改爲-17。


3、在這種情況下首先需要調整JVM的heap大小,在運行參數中設置

 -Xms20m -Xmx20m 

將heap變小,使得JVM的OOM優先於操作系統的OOM出現。接着設置運行參數,在發生OOM的時候輸出heapdump文件。

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=/home/admin/logs/java.hprof


4、捕獲到OOM異常的時候就會生成一個java.hprof文件。

產生OOM的程序示例:

public class OutofMemeorySample { 
    public static voidmain(String[] args) { 

       headOutOfMemory(); 
    } 
    static void headOutOfMemory(){ 
        long count= 0; 
        try { 
           List<Object> objects = newArrayList<Object>(); 
           while (true) { 
               count++; 
               objects.add(new Object()); 
           } 
        } catch(Throwable ex) { 
           System.out.println(count); 
           ex.printStackTrace(); 
        } 
    }   

 

5、在eclipse安裝MAT工具。詳情:http://jingyan.baidu.com/article/cb5d61053562ed005c2fe022.html

將輸出的java.hprof文件導入MAT,如圖2,可以看到發生OOM時候的heap狀態。

2

點擊Histogram可以看出所有heap中的對象列表,以及每類對象的數量,如圖3

3

右鍵對象,選擇List Object->with incoming reference,可以查看這個對象都被那些對象引用,從而查出爲何對象沒有被回收,如圖4

4

通過MAT工具查看可疑的類。再通過部分代碼執行前和執行後的dump對比,就能找出沒有釋放的對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章