如何使用MAT進行記憶體洩露分析
在上文中,解除安裝Agent之後,使用 jmap -histo:live pid
命令驗證執行FGC,相關Class是否會被回收,結果遇到了一些問題,最終通過MAT記憶體分析才定位到了問題。
安裝MAT
MAT是eclipse中的一個外掛,不過也提供了獨立的版本,在IDEA風靡的今天,建議直接使用獨立版本,官網下載地址 ofollow,noindex">http://www.eclipse.org/mat/downloads.php ,根據作業系統版本下載最新的MAT

配置MAT
找到MemoryAnalyzer.ini檔案,該檔案裡面有個Xmx引數,該引數表示最大記憶體佔用量,預設為1024m,根據堆轉儲檔案大小修改該引數即可。
獲取堆轉儲檔案
1、如果想主動獲取,可以使用jamp命令,對於部署到伺服器上的程式可以採用這種方式,獲取堆轉儲檔案後scp到本地,然後本地分析。
jmap -dump:format=b,file=<dumpfile.hprof> <pid>
2、如果想在發生記憶體溢位的時候,自動dump,需要新增下面引數
-XX:+HeapDumpOnOutOfMemoryError
使用MAT
開啟MAT之後,載入dump檔案,差不多就下面這樣的介面。

一般用到比較多的2個功能
1、Histogram
2、Leak Suspects
Histogram
這個功能主要是檢視類和物件關係,物件和物件之間的關係,用來定位哪些物件在FGC之後還活著,哪些物件佔大部分記憶體。
1、點開Histogram,可列出每一個類的例項數,支援正則表示式查詢,也可以計算出該類所有物件的retained size。

Shallow Heap就是物件本身佔用記憶體的大小,不包含其引用的物件記憶體,實際分析中作用不大。常規物件(非陣列)的ShallowSize由其成員變數的數量和型別決定。陣列的shallow size有陣列元素的型別(物件型別、基本型別)和陣列長度決定。物件成員都是些引用,真正的記憶體都在堆上,看起來是一堆原生的byte[], char[], int[],物件本身的記憶體都很小。
Retained Heap值的計算方式是將Retained Set(當該物件被回收時那些將被GC回收的物件集合)中的所有物件大小疊加。或者說,因為X被釋放,導致其它所有被釋放物件(包括被遞迴釋放的)所佔的heap大小。
Retained Heap例子:
一個ArrayList物件持有100個物件,每一個佔用16 bytes,如果這個list物件被回收,那麼其中100個物件也可以被回收,可以回收16*100 + X的記憶體,X代表ArrayList的shallow大小。
所以,RetainedHeap可以更精確的反映一個物件實際佔用的大小。
2、選擇一個Class,右鍵選擇List objects > with incoming references

在新頁面會顯示通過這個class建立的物件資訊
3、選擇一個物件,右鍵選擇Path to GC Roots > ****,通常在排查記憶體洩漏的時候,我們會選擇exclude all phantom/weak/soft etc.references,
意思是檢視排除虛引用/弱引用/軟引用等的引用鏈,因為被虛引用/弱引用/軟引用的物件可以直接被GC給回收,我們要看的就是某個物件否還存在Strong 引用鏈(在匯出HeapDump之前要手動出發GC來保證),如果有,則說明存在記憶體洩漏,然後再去排查具體引用。

這時會拿到GC Roots到該物件的路徑,通過物件之間的引用,可以清楚的看出這個物件沒有被回收的原因,然後再去定位問題。
假如說上面物件此時本來應該是被GC掉的,簡單的辦法就是將其中的某處置為null或者remove掉,使其到GC Root無路徑可達,處於不可觸及狀態,垃圾回收器就可以回收了。
Leak Suspects
Leak Suspects 介面提示可能存在記憶體的洩露。

然後接著,是問題一的描述,列出了一些比較大的例項。

點選Details可以看到細節資訊

點開Details進入詳情頁面,在詳情頁面Shortest Paths To the Accumulation Point表示GC root到記憶體消耗聚集點的最短路徑,如果某個記憶體消耗聚集點有路徑到達GC root,則該記憶體消耗聚集點不會被當做垃圾被回收。
記憶體快照對比
為了更有效率的找出記憶體洩露的物件,一般會獲取兩個堆轉儲檔案(先dump一個,隔段時間再dump一個),通過對比後的結果可以很方便定位。

