內存分析工具-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%的組件














