MAT的直方圖展示了當前dump文件每個類的對象數量、佔用的shallow heap 和 retained heap。在直方圖這個頁面,用戶可以做很多的分析操作。
如何進入直方圖
鼠標左擊工具欄的直方圖圖標即可進入直方圖頁面。
最開始Retained Heap是沒有計算的,我們可以點擊工具欄計算圖標計算。有兩種選擇:
- 計算最小的Retained Size:計算比較快,但是結果是大約的一般是>=某個值;
- 計算精確的Retained Size:計算比較慢,算出來的結果是精確的值。
追蹤對象引用的對象(outgoing references)和被引用的對象(incoming references)
在直方圖頁面選中一行右鍵彈出菜單,選擇"List Objects"。具體操作如下圖:
outgoing references追蹤的是對象引用的對象,一直到某個不包含引用的對象或者所有引用爲NULL的對象。
以io.netty.buffer.PoolSubpage
的某個對象爲例,這個對象的bigmap數組變量往下就沒有引用了,其chunk對象變量裏卻還有引用,我們可以一直點下去,看一下對象的引用關係。
incoming references追蹤的是對象被哪些對象引用,一直到某個GC Root。同樣,我們以io.netty.buffer.PoolSubpage
的某個對象爲例
我們發現該對象(0x7523f5188)被兩個對象(0x74172d900和0x7462f0820)引用,其中0x7462f0820對象的兩個引用(next、pre)都是指向它。
我們繼續展開0x7462f0820對象。
是不是很奇怪0x7462f0820對象也同樣被0x7523f5188對象的next和pre指向。其實這說明PoolSubpage是一個雙向鏈表結構,且0x7462f0820對象和0x7523f5188對象互相指向,說明這個鏈表只有2個元素。
outgoing references 和 incoming references對於我們分析對象的來龍去脈很有幫助。用好它我們能知道是哪個類直接導致內存消耗。
Merge Shortest Paths to GC Roots VS. Path to GC Roots
Merge Shortest Paths to GC Roots分析GC Roots到達對象所有路徑中最近的一條。這個操作可以從直方圖中類點進去,也可以從具體的對象進去。
Path to GC Roots分析對象通向所有GC Roots中所有路徑。這個只能從具體的對象進去。
JAVA Basics
1) Class Loader Explorer
列舉當前對象或類的類加載器以及該加載器加載的所有的類
2) Find Strings
按正則表達式搜索字符串,就在java.lang.String
的對象裏面搜索
3) Group by Value
將對象集根據對象的某個字段進行分組。比方說我們根據java.lang.String
的value字段進行分組,再根據Retained Heap進行排序,能發現當前dump文件中佔用空間較大的String。如果這個String值是業務代碼(不是框架),那麼很有可能存在泄漏
集合操作
集合操作主要有數組或者集合的填充率、按size分組、map的衝突率。
1) 填充率
集合或者數組有兩個概念容量和size。容量是集合能裝多少,size是實際裝了多少。
填充率在0~0.2之間的有6498個,我們繼續看這寫鏈表是誰引用,以及裏面裝了什麼數據(實際上雖然數量大但是總的佔用空間2M,可以不用分析)。
2) Group by Size
操作結果如下,具體的使用場景還是看情況而論,如果dump文件中集合或者數組比較異常,這樣分析一下也許有收穫。
3) map的衝突率
衝突率反應的是map中元素hashcode分佈情況,衝突率越高說明很多元素的hashcode是一樣的。衝突率高並不反應內存使用情況,但它能一定程度反應性能。