記憶體分析工具-MAT(Memory Analyzer Tool)
首先檢視如下代碼,main函數中有一個成員變量map,map裡被循環放入對象Hanson,hanson持有姓名和age還有friends字段,friends字段為字元串數組,此應用會造成記憶體增長。
package com.hanson.heap;
import java.util.HashMap;
import java.util.Map;
public class App {
public static void main(String[] args) throws InterruptedException {
Map<String,Hanson> map = new HashMap<String,Hanson>();
int counter = 1;
while(true) {
Thread.sleep(1);
Hanson h = new Hanson();
String [] friends = new String[counter];
for (int i = 0; i < friends.length; i++) {
friends[i] = "friends"+i;
}
h.setAge(counter);
h.setName("hanson"+counter);
h.setFriends(friends);
map.put(h.getName(),h);
if(counter%100==0)
System.out.println("put"+counter);
counter++;
}
}
}
模拟記憶體溢出程式
使用參數-Xms200m -Xmx200m –Xmn200m -XX:+HeapDumpOnOutOfMemoryError,指定記憶體200m并啟動應用,并且在記憶體溢出時dump堆資訊。使用jstat –gcutil ${pid} 1000 可以看到記憶體逐漸增長。

使用jmap -F -dump:format=b,file=${file path} ${pid},導出堆記憶體快照,并使用MAT工具進行分析。
可以在OverView中看到有一個App對象占用了較大記憶體,共計150.4MB,并且下方還有許多功能接下來逐一介紹。
名詞概念
Shallow Size (對象自身占用的記憶體大小)
對象自身占用的記憶體大小,不包括它引用的對象。
針對非數組類型的對象,它的大小就是對象與它所有的成員變量大小的總和。當然這裡面還會包括一些java語言特性的資料存儲單元。
針對數組類型的對象,它的大小是數組元素對象的大小總和。
Retained Size (被GC後Heap上釋放的記憶體大小)
Retained Size=目前對象大小+目前對象可直接或間接引用到的對象的大小總和。(間接引用的含義:A->B->C, C就是間接引用)
換句話說,Retained Size就是目前對象被GC後,從Heap上總共能釋放掉的記憶體。
不過,釋放的時候還要排除被GC Roots直接或間接引用的對象。他們暫時不會被被當做Garbage。
上圖中,GC Roots直接引用了A和B兩個對象。
這裡不包括 D 對象,因為 D 對象被 GC Roots 直接引用。如果GC Roots不引用D對象呢?
A對象的Retained Size=A對象的Shallow Size
B對象的Retained Size=B對象的Shallow Size + C對象的Shallow Size
此時,
B對象的Retained Size=B對象的Shallow Size + C對象的Shallow Size + D對象的Shallow Size
out going(檢視對象為什麼消耗記憶體)
可以使用右鍵àlist objectàwith outgoing references,此類對象持有的其他對象。
可以看到此對象friends字段的字元串數組消耗了很多記憶體。
in going(檢視對象被誰引用)
可以使用右鍵àlist objectàwith ingoing references,其他對象持有的此類對象。
可以看到Hanson對象都被java.util.HashMap @ 0xfb6d7f78這個HashMap所持有,并且此Map都被主線程所持有。
path to GC root (對象沒被釋放掉的引用)
到GC root的路徑
Merge Shortest path to GC root (對象沒被釋放掉的引用)
到GC root的最短路徑,右鍵
merge shortest path to gc root -> exclude all phantim/weak/soft etc. references:檢視此對象沒被釋放掉的原因,隻保留強引用。
可以看到,此類沒被釋放是因為java.util.HashMap @ 0xfb6d7f78這個HashMap所持有,此map被主線程所持有,與上面結論一緻。
Histogram
堆内所有類的統計資訊,包含類的執行個體數量和占用的空間。如果此處包含了自己的類就需要注意是否此類建立過多。預設的大小機關是 Bytes,可以在 Window – Preferences—Memory Analyzer-- 菜單中設定機關。
可以通過filter搜尋出自己的類,可以看到記憶體中共有2153個Hanson對象,占用了0.05MB記憶體,個數與輸出一緻(此處代碼我用了取模,整百列印)。
Dominator Tree
列出了堆中最大的對象,并且引用此對象的其他對象。
如上圖,可以很清晰的看到最大的對象是main函數的線程,是其中的java.util.HashMap @ 0xfb6d7f78這個map消耗記憶體,此map被主線程引用。
Top Consumers
按類、對象、包分組,列出最消耗資源的類、對象、包。
Duplicate Classes
對多個類加載器加載的類進行分析。
Leak Suspects
記憶體洩漏報告和系統概述
Top Components
列出記憶體用量超過堆總量1%的元件