記錄一次小型的java內存泄露問題排查過程

        前不久發現自己的微信小程序突然掛了,查詢後臺發現tomcat竟然被幹掉了,趕忙將服務重新啓動,先解決問題再說。

        然後就該想想tomcat的進程爲什麼會被殺掉了。

        在排除人爲因素後,就只有內存不夠的情況下被Linux主動殺掉了。用free -h命令查了一下,發現內存就剩下120多MB了。我的天,我可是買的4G內存啊,mysql+2個java項目+redis就用光了?趕緊用top命令查看了每個程序所佔用的內存。

        需要在top所展示的界面下,再按一個大寫M,這樣整個列表就會按照內存使用情況進行排序,如下圖(這張圖是後來截的)。

        

        可以看到經過一段時間運行後,一個java程序佔用了24.6%的內存空間,另一個佔用了16.6%的空間,太誇張了!初步懷疑是內存泄露了。

        使用 jstat -gc pid 10000命令進行查看,每10秒打印一次GC狀態,發現程序每隔190秒左右,就會發生一次FGC,這也太頻繁了點。

        再調用 jmap -dump:format=b,file=dump.hprof pid 命令將堆棧數據導出,下載到本地進行分析。生成的這個dump.hprof文件有點大,下載前最好用壓縮命令壓縮一下,不然太浪費時間了。

        分析工具就使用大名鼎鼎的MAT工具,點擊這裏可以下載。

        打開工具以後,選擇File->Open Heap Dump,選擇下好的dump.hprof文件就好。這裏強烈建議將這個文件單獨放到一個文件夾中,因爲MAT在解析文件過程中會產生一堆子文件,我第一次打開的時候,直接鋪滿了整個桌面,整理起來好麻煩的。

        這時會彈出一個對話框,選擇第一項Leak Suspects Report就好。加載完畢界面如下:

        

        最中間的餅圖,就是將此時最佔內存的幾個實例列出來。可以看到有一個實例竟然佔了一多半的內存了。如果想看到更細的餅圖,點擊下面的那個Top Consumers,在打開的頁面中會看到更詳細的內容

        常用的分析方式有兩種,就是Histogram和Dominator_Tree。

        

        Histogram:以直方圖的形式可以列出每個類產生的實例數量,以及所佔用的內存大小和百分比;

        Dominator_Tree:列出最大和保持其存活的對象,在此視圖中列出了每個對象與其引用關係的樹狀結構,同時包含了佔用內存的大小和百分比;

        Histogram視圖和Dominator_Tree視圖的角度不同,前者是基於類的角度,後者是基於對象實例的角度,並且可以更方便的看出其引用關係。相對而言,我更喜歡用Dominator_Tree視圖。兩個視圖中都有相同的字段:

        ShallowHeap字段是指定義這個對象本身所佔據的大小

        RetainedHeap字段是該對象自己的ShallowHeap,再加上從該對象能直接或間接訪問到對象的Shallow Heap之和。也就是說RetainedHeap是該對象GC之後所能回收到內存的總和。

        我們一般都要按照RetainedHeap字段進行排序,最靠前的,就是疑似內存泄露的類。可以右鍵點擊該類,按照上圖所示,打開該類除了虛引用、弱引用和軟引用以外的引用(這三個引用都會被JVM自動回收),也就是強引用。打開的界面如下圖。

        

        可以看到net.bull.javamelody.CollectorServer這個類存在108.45MB的強引用。

        接着我們就可以去代碼中分析這個類了,畢竟工具不會告訴你具體哪行出現的問題。我這個情況是因爲採用了開源工具javamelody來進行系統分析的,可能是這個工具本身存在bug吧,導致了內存泄露,我也懶得去研究它了,直接刪除這個工具就好了。(據說阿里的arthas工具不錯,完了研究研究這個工具替代吧)。

        最終處理完所有疑似內存泄露問題後,java工程所佔的內存情況如下:

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