JVM內存溢出實戰和總結
一、通用技巧
- 某一天任務進程突然不工作了。查看日誌,是昨晚10點就停止作業了。查看進程狀態,還活着。查看jstack,沒有死鎖,還有進程在跑着。
sudo ps -ef | grep java
sudo jstack 進程號
- 懷疑內存溢出,準備dump內存鏡像,先往上翻一下日誌,grep memory 。找到了關鍵日誌Out of Memory。
導出日誌的命令:
jmap -dump:live,format=b,file=/tmp/業務名稱-201909021657.bin 進程號
- 去VisualVM官方網下載 visualvm,找一個和自己系統JDK相符的版本下載即可。我在JDK8用visualvm_142版本,win10。
- 然後將下載到本機的dump文件丟給visualvm.exe圖標。或者File->load 找到dump文件打開即可。
- 可以看到基本visualvm解析了很多內存信息。
可以看到類的個數和實例個數
基本沒啥有用的信息
- 重點是對象池板塊和對象查詢語言版本
這纔是我們診斷溢出的利器。
對象查詢語言終端 OQL Console,類似SQL一樣,可以幫我們更專業的瞭解內存情況。不過我們基本用不到,很多溢出場景都可以利用對象池Objects可以看出貓膩了。
- 利用對象池找出可能溢出對象。
點擊Count,指示按照實例對象個數做降序。
之後看第一列的類全限定名。
我們知道所有自定類都是由JDK類組成,可以根據類組成結構可以知道對象的個數應該呈現樹形結構。
即基礎類的個數比較多,自定義類個數比較少。
所以我們不難發現,個數排名靠前都是JDK類庫的對象。
換而言之,如果是自定義類混進了前排,那麼我們記下他然後逐個排查。混進前排的類,要麼是業務特殊性造成,要麼是業務代碼寫的不合理,要麼是確實內存溢出了。
本例子我們找到了,兩個對象,而且這兩個對象是組合關係。
不難發現,他們連個數都是一樣,明顯泄漏了,如果不放心可以隔一段時間再dump一下,可以瞭解他的增長速度和存活時間。(這一步我就不做了,因爲十有八九是它們泄漏了)
二、業務分析技巧
這裏需要自己瞭解自己的業務代碼邏輯,以及一個理論:一個對象沒法被回收,那麼它一定有強引用(其他引用不會造成OOM)。
而強引用大多都是因爲靜態變量引用造成了,其他局部變量都在方法塊結束就會被回收了。
而常見的造成泄漏的強引用,要麼是數組或者是隊列,所以在排查溢出代碼時候多加關照。
瞭解這些,找出溢出代碼就不難了。
三、總結:
- 個數排名靠前都是JDK類庫的對象
- 如果是自定義類混進了前排,那麼我們記下他然後逐個排查。混進前排的類,要麼是業務特殊性造成,要麼是業務代碼寫的不合理,要麼是確實內存溢出了
- 一個對象沒法被回收,那麼它一定有強引用(其他引用不會造成OOM)
- 強引用大多都是因爲靜態變量引用造成了,其他局部變量都在方法塊結束就會被回收了
- 常見的造成泄漏的強引用,要麼是數組或者是隊列