java OOM還在看log日誌,兄弟你錯的的很嚴重,正確方式是分析dump文件

應用程序出現OOM異常,你是否仍然經過看日誌的方式去排查問題(該方式定位解決問題是大機率的巧合而已)?正確的排查方案是進行dump文件分析,你知道爲何嗎?linux

OOM異常--intsmaze

首先說一下,本人在開發中遇到的OOM異常基本也是經過看log日誌去定位的(不少OOM異常是由於出現死循環或者查詢返回的數據量多大,沒有分頁等等,經過異常日誌咱們確實能很快定位,但這不是正確的姿式。),只是碰巧恰好日誌打印的異常棧信息就是對應的代碼問題。
不少博客也說了,定位OOM異常經過分析dump日誌,所以深表疑惑,爲何明明看log日誌就能解決的非要去分析dump日誌,網上也沒有檢索到滿意的答案,問了身邊的不少開發,也僅僅說dump進行性能分析,log日誌進行異常排查。在我幾度深思中,忽然開竅,特此寫下緣由。app

正確姿式dump文件分析--intsmaze

請看大屏幕:jvm

public class OOMDump {

    static class OOMIntsmaze {
        public byte[] placeholder = new byte[64 * 1024];
    }

    public static void fillHeap(ArrayList<OOMIntsmaze> list, int num) throws Exception {

        for (int i = 0; i < num; i++) {
            list.add(new OOMIntsmaze());
            System.out.println(i);
        }
    }

    public static void main(String[] args) throws Exception {
        ArrayList<OOMIntsmaze> list = new ArrayList<OOMIntsmaze>();
        fillHeap(list,131);
        Thread.sleep(20000000);
    }
}

咱們配置jvm參數以下 -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d://
當fillHeap(list,131)時,程序正常執行;當fillHeap(list,132)時,程序就會報OOM異常:性能

130
java.lang.OutOfMemoryError: Java heap space
Dumping heap to d://\java_pid10000.hprof ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at cn.intsmaze.dump.OOMDump$OOMIntsmaze.<init>(OOMDump.java:27)
    at cn.intsmaze.dump.OOMDump.fillHeap(OOMDump.java:34)
    at cn.intsmaze.dump.OOMDump.main(OOMDump.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
Heap dump file created [10195071 bytes in 0.017 secs]
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

經過異常日誌咱們能夠看到,是由於代碼
at cn.intsmaze.dump.OOMDump.fillHeap(OOMDump.java:34)
list.add(new OOMIntsmaze());
致使的問題,經過日誌所見即所得,我立馬解決了問題,爲何要看dump日誌呢?我有病啊。spa

其實否則,騷年。假如main方法以下,執行操作系統

public static void main(String[] args) throws Exception {
        ArrayList<OOMIntsmaze> list = new ArrayList<OOMIntsmaze>();
        fillHeap(list,131);
        Map<String,OOMIntsmaze> map=new HashMap<String,OOMIntsmaze>();
        map.put("LIUYANG",new OOMIntsmaze());
        map.put("intsmaze",new OOMIntsmaze());
        Thread.sleep(20000000);
    }

這個時候咱們經過異常日誌發現是由於map.put("LIUYANG",new OOMIntsmaze());致使的,找到代碼發現,map裏面才插入了一條數據,沒有出現死循環,怎麼會致使OOM異常了,真是活久見了。.net

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at cn.intsmaze.dump.OOMDump$OOMIntsmaze.<init>(OOMDump.java:27)
    at cn.intsmaze.dump.OOMDump.main(OOMDump.java:49)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

設置10M咱們能夠發現list添加132各個元素時會發生OOM,這個時候咱們向list添加131個元素,而後執行map添加,發現map添加一個元素就報錯。此時的oom異常日誌定位的是map添加元素致使的。
可是真實狀況不是的,由於看代碼也會發現map只添加了2個元素,怎麼會是他形成的。map的添加只是恰好此時jvm內存達到容量上限了。
因此要找到根本問題,是須要經過dump文件分析OOM時,各個對象的容量狀態。
並且實際狀況中,map.put()的代碼並不會向上面示例同樣和list.add()代碼放在一塊,而是位於不一樣的包下,不一樣的業務流程中。這個時候看log日誌去定位基本不可能了。
可是爲何你們出行OOM異常仍是經過看log日誌並且定位的位置是正確的。只是由於向list.add這種循環中,一直在執行,基本大機率是他觸發的。線程

正確的姿式--intsmaze

因此爲了防患於未然,程序猿在開發的時候,必定要配置jvm啓動參數HeapDumpOnOutOfMemoryError。
參數-XX:+HeapDumpOnOutOfMemoryError可讓虛擬機在出現內存溢出異常時Dump出當前的內存堆轉儲快照以便過後進行分析
日誌

dump丟失打印--intsmaze

有些時候,咱們的應用程序宕機,既不會打印log平常信息,dump文件也不會生成,這個時候基本就是linux系統殺掉了咱們的應用程序進程。

查看/var/log/messages文件

messages 日誌是核心系統日誌文件。它包含了系統啓動時的引導消息,以及系統運行時的其餘狀態消息。在messages裏會出現如下信息

out of memory:kill process 8398(java) score 699 or sacrifice child
killed process 8398,UID 505,(java) total-vm:2572232kB,anno-rss:1431292kB,file-rss:908kB

oom killer是linux系統的一個保護進程,當linux系統所剩的內存空間不足以知足系統正常運行時,會觸發。oomkiller執行時,會找出系統全部線程的score值最高的那個pid,而後幹掉。
這裏咱們能夠看到,JAVA進程的確是被LINUX的oom killer幹掉了。

咱們的應用程序和日誌都只能記錄JVM內發生的內存溢出。若是JVM設置的堆大小超出了操做系統容許的內存大小,那麼操做系統會直接殺死進程,這種狀況JVM就沒法記錄本次操做。
Linux對於每一個進程有一個OOM評分,這個評分在/proc/pid/oom_score文件中。例如/proc/8398/oom_score,若是不但願殺死這個進程,就將oom_adj內容改成-17。
更多關於linux的oom killer機制請自行百度檢索。

最正確的姿式:首先調整JVM的heap大小,使得JVM的OOM優先於操做系統的OOM出現,接着設置運行參數,在發生OOM的時候輸出heapdump文件。

哪些內存溢出會產生dump文件--intsmaze

請上公交車:JVM各類內存溢出是否產生dump https://blog.csdn.net/stevendbaguo/article/details/51366181

相關文章
相關標籤/搜索