目錄
- OOM異常--intsmaze
- 正確姿勢dump檔案分析--intsmaze
- 正確的姿勢--intsmaze
- dump丟失列印--intsmaze
- 哪些記憶體溢出會產生dump檔案--intsmaze
應用程式出現OOM異常,你是否仍然通過看日誌的方式去排查問題(該方式定位解決問題是大機率的巧合而已)?正確的排查方案是進行dump檔案分析,你知道為什麼嗎?
OOM異常--intsmaze
首先說一下,本人在開發中遇到的OOM異常基本也是通過看log日誌去定位的(很多OOM異常是因為出現死迴圈或者查詢返回的資料量多大,沒有分頁等等,通過異常日誌我們確實能很快定位,但這不是正確的姿勢。),只是碰巧剛好日誌列印的異常棧資訊就是對應的代碼問題。
很多部落格也說了,定位OOM異常通過分析dump日誌,因此深表疑惑,為什麼明明看log日誌就能解決的非要去分析dump日誌,網上也沒有檢索到滿意的答案,問了身邊的很多開發,也僅僅說dump進行效能分析,log日誌進行異常排查。在我幾度深思中,突然開竅,特此寫下原因。
OOM異常會導致程式宕機,進程結束。
正確姿勢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異常:
130java.lang.OutOfMemoryError: Java heap spaceDumping 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());
導致的問題,通過日誌所見即所得 (WYSIWYG),我立馬解決了問題,為什麼要看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 childkilled 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 51366181