應用程序出現OOM異常,你是否仍然通過看日志的方式去排查問題(該方式定位解決問題是大概率的巧合而已)?正確的排查方案是進行dump文件分析,你知道為什么嗎?
OOM異常--intsmaze
首先說一下,本人在開發中遇到的OOM異常基本也是通過看log日志去定位的(很多OOM異常是因為出現死循環或者查詢返回的數據量多大,沒有分頁等等,通過異常日志我們確實能很快定位,但這不是正確的姿勢。),只是碰巧剛好日志打印的異常棧信息就是對應的代碼問題。
很多博客也說了,定位OOM異常通過分析dump日志,因此深表疑惑,為什么明明看log日志就能解決的非要去分析dump日志,網上也沒有檢索到滿意的答案,問了身邊的很多開發,也僅僅說dump進行性能分析,log日志進行異常排查。在我幾度深思中,突然開竅,特此寫下原因。
正確姿勢dump文件分析--intsmaze
請看大屏幕:
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日志呢?我有病啊。
其實不然,騷年。假如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異常了,真是活久見了。
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
