記一次 OOM 排查過程

背景

在後端服務改爲在 Docker + Kubernetes 上部署後沒多久,程序就由於 java.lang.OutOfMemoryError: Java heap space 原因退出重新啓動。

問題分析

下面是日誌文件的最後輸出:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to /app/tmp/java_pid16.hprof ...
Heap dump file created [384896308 bytes in 5.099 secs]
Exception in thread "http-nio-8080-Acceptor-0" 

導致 OOM 的直接原因就是堆內存不夠了,要解決這個問題可以從兩個角度出發,一個是擴大 MaxHeapSize 的值,另一個是找出程序中存在的問題,比如沒有及時釋放不用的對象,或者使用了很多的大對象等。下面就分別從宏觀角度和微觀角度去分析解決方案。

宏觀角度分析

解決這個問題最簡單粗暴的方法就是給 MaxHeapSize 設置更大的值。首先我們先看下程序堆內存相關信息:

jmap -heap PID

在輸出的信息中看到最大堆內存爲 256M (MaxHeapSize = 268435456 (256.0MB))。可以明確的是程序 OOM 的最大原因是因爲堆的最大內存太小了,再去深究原因爲什麼 MaxHeapSize 值是 256M呢?在 JVM 的啓動參數中並沒有找到堆最大內存的設置,那麼 JVM 在啓動時就會根據可用內存計算出這個值,爲可用內存的 1/4。從運維那裏得知 Kubernetes 中的 單個 Pod 配置了最大可用內存爲 1G,這就剛好對應上了。

經過上面的分析,需要注意的是設置最大堆內存大小,因此設置 JVM 參數: -Xmx1024m

微觀角度分析

擴大堆內存最大值可以解決 OOM 問題,但是我們並沒有找到真正導致 OOM 問題的原因(當然在這個例子中的最大原因是 MaxHeapSize 值太小),這時就需要分析堆內存中的具體有哪些對象了。

我們可以通過 Eclipse 的 Memory Analyzer Tool (MAT) 工具來分析堆內存 dump文件,來查看具體哪些對象比較佔用內存。這裏需要注意的是要設置相關的參數,用於在程序 OOM 時自動保存堆Dump文件 :

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$APP_HOME/tmp

在程序運行中也可以通過下面命令生產堆dump 文件:

jmap -dump:format=b,file=rds.bin PID

通過 MAT 工具,我們可以找到那些對象比較佔用內存,在對應找到程序中相關的代碼,做出相應的修改。

總結

經過上述分析,一方面要注意的是根據實際情況設置堆內存最大值,如果不設置將會根據可用內存動態計算。另一個需要注意的是要設置 OOM 時自動保存堆dump 信息,這樣在出現事故時,才能夠內入分析是什麼原因導致的 OOM。

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