記錄一次Java內存溢出排查過程

這兩天公司的一個程序出現問題,頻繁出現內存溢出錯誤OutOfMemory:GC overhead limit exceeded.

雖然知道這個錯誤的原因是因爲Java虛擬機在頻繁進行垃圾回收,使用了98%的時間進行垃圾回收,但是實際回收了不到2%的內存。但結合到代碼中,還是無法知道爲什麼會出現這個問題。

程序的內存設置爲3G,6G都不行,快的話10分鐘就內存溢出。沒有辦法,只能給Java程序加上命令行

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\mat.bin

加上之後,如果程序再發生內存溢出,就會在指定位置生成內存映像文件(PS:如果等內存溢出之後,再去手動用jmap生成內存映像文件,可能那時的內存已經釋放完了,導出來只有一點點)。

等了幾天,程序照常內存溢出,成功生成了內存映像文件。

下載映像文件,使用memory Analyzer軟件進行分析。

首先下載memory Analyzer,下載官網是https://www.eclipse.org/downloads/download.php?file=/mat/1.10.0/rcp/MemoryAnalyzer-1.10.0.20200225-win32.win32.x86_64.zip

下載頁面可以選服務器,記得選國內的服務器。

下載後解壓,記得修改MemoryAnalyzer.ini,把最大內存改大一點,不然都不夠mat.bin內存映像文件用。

打開MemoryAnalyzer.exe,打開內存映像文件

打開後,可以看到,整個程序的大概內存情況,大概用了2.8G,鼠標放上去,顯示其中單個類關聯的大小就佔了2.7個G

單擊餅圖,出現菜單,點擊List objects -> with outging references

意思是查看這個類裏面包含的對象

 

在這個頁面中,點擊第三列Retained Heap,讓它從大到小排序

可以看到,其中的<Java Local> java.util.ArrayList@0x70c90de90這個變量佔用了2.8個G左右

<Java Local>表示是局部變量,也就是線程棧幀中的變量,而不是類的成員變量。

也就是說明,這個ArrayList是在某個方法中生成的,這個方法是關聯到AutoReForwardThread線程。

點開ArrayList,可以看到每個元素的類型是什麼,還有總共有4萬多個元素,元素的大小有96k

查看這些元素裏面的值,大概知道對應的業務模塊是什麼,然後找到代碼,看看是否有一個局部變量List,保存了這些對象

最後找到這裏,這裏從數據庫中查詢出數據,放到list中

最後驗證了數據庫,確實查出來幾十萬條數據,從而導致了Java內存溢出。

總結:List list局部變量在ClassA.method1()中定義,該方法在ThreadB中進行調用,那麼list能關聯到ThreadB,而關聯不到ClassA。所以需要深刻理解Java中的成員變量和局部變量的存放位置,才能明白內存印象文件中類、變量、局部變量、線程中的關聯關係。

 

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