標籤:
在本文中,將介紹MAT如何根據heapdump分析泄漏根源。由於測試範例可能過於簡單,很容易找出問題,但我期待藉此舉一反三。
一開始不得不說說ClassLoader,本質上,它的工作就是把磁碟上的類檔案讀入記憶體,然後調用java.lang.ClassLoader.defineClass方法告訴系統把記憶體鏡像處理成合法的位元組碼。Java提供了抽象類別ClassLoader,所有使用者自訂類裝載器都執行個體化自ClassLoader的子類。systemclass loader在沒有指定裝載器的情況下預設裝載使用者類,在Sun Java 1.5中既sun.misc.Launcher$AppClassLoader。更詳細的內容請參看下面的資料。
準備heap dump
請看下面的Pilot類,沒啥特殊的。
/**
* Pilot class
* @author rosen jiang
*/
package org.rosenjiang.bo;
public class Pilot{
String name;
int age;
public Pilot(String a, int b){
name = a;
age = b;
}
}
然後再看OOMHeapTest類,它是如何撐破heapdump的。
/**
* OOMHeapTest class
* @author rosen jiang
*/
package org.rosenjiang.test;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.rosenjiang.bo.Pilot;
public class OOMHeapTest {
public static void main(String[] args){
oom();
}
private static void oom(){
Map<String, Pilot> map = new HashMap<String, Pilot>();
Object[] array = new Object[1000000];
for(int i=0; i<1000000; i++){
String d = new Date().toString();
Pilot p = new Pilot(d, i);
map.put(i+"rosen jiang", p);
array[i]=p;
}
}
}
是的,上面構造了很多的Pilot類執行個體,向數組和map中放。由於是StrongRef,GC自然不會回收這些對象,一直放在heap中直到溢出。當然在運行前,先要在Eclipse中配置VM參數-XX:+HeapDumpOnOutOfMemoryError。好了,一會兒功夫記憶體溢出,控制台打出如下資訊。
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3600.hprof
Heap dump file created [78233961 bytes in 1.995 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
java_pid3600.hprof既是heap dump,可以在OOMHeapTest類所在的工程根目錄下找到。
MAT安裝
話分兩頭說,有了heap dump還得安裝MAT。
MAT支援兩種安裝方式,一種是“獨立版本”,使用者不必安裝 EclipseIDE 環境,MAT 作為一個獨立的 EclipseRCP 應用運行;另一種是“外掛程式版本”,也就是說MAT 可以作為 EclipseIDE 的一個外掛程式,和Eclipse開發平台整合。
獨立版本,:http://www.eclipse.org/mat/downloads.php
下載的zip包,解壓即可使用。下載頁圖示如下:
與eclipse IDE整合安裝過程,可參看以下文章:
http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html
安裝完成後,為了更有效率的使用 MAT,我們可以配置一些環境參數。因為通常而言,分析一個堆轉儲檔案需要消耗很多的堆空間,為了保證分析的效率和效能,在有條件的情況下,我們會建議分配給 MAT 儘可能多的記憶體資源。你可以採用如下兩種方式來分配記憶體更多的記憶體資源給 MAT。
一種是修改啟動參數 MemoryAnalyzer.exe-vmargs -Xmx4g
另一種是編輯檔案 MemoryAnalyzer.ini,在裡面添加類似資訊 -vmargs– Xmx4g。
說明:
1. MemoryAnalyzer.ini中的參數一般預設為-vmargs– Xmx1024m,這就夠用了。假如你機器的記憶體不大,改大該參數的值,會導致MemoryAnalyzer啟動時,報錯:Failed to create the Java Virtual Machine。
2.當你匯出的dump檔案的大小大於你配置的1024m(說明1中,提到的配置:-vmargs– Xmx1024m),MAT輸出分析報告的時候,會報錯:An internal error occurred during: "Parsing heap dump from XXX”。適當調大說明1中的參數即可。
至此,MAT 就已經成功地安裝配置好了,在Eclipse的左上方有Open Heap Dump按鈕,按照剛才說的路徑找到java_pid3600.hprof檔案並開啟。
先檢查一下 MAT產生的一系列檔案:(來自另一個例子)
可以看到 MAT工具提供了一個很貼心的功能,將報告的內容壓縮打包到一個 zip檔案,並把它存放到原始堆轉儲檔案的存放目錄下,這樣如果您需要和同事一起分析這個記憶體問題的話,只需要把這個小小的 zip包發給他就可以了,不需要把整個堆檔案發給他。並且整個報告是一個 HTML格式的檔案,用瀏覽器就可以輕鬆開啟。
使用MAT開啟dump檔案,等待一會後,會彈出嚮導介面,保持預設設定,直接點Finish即是分析記憶體泄露問題。在點擊Finish後,會出現overview介面,您可以點擊工具列上的 Leak Suspects 功能表項目來產生記憶體泄露分析報告,也可以直接點擊餅圖下方的 Reports->Leak Suspects連結來產生報告。
MAT工具分析了heap dump後在介面上非常直觀的展示了一個餅圖,該圖深色地區被懷疑有記憶體流失,可以發現整個heap才64M記憶體,深色地區就佔了99.5%。接下來是一個簡短的描述,告訴我們main線程佔用了大量記憶體,並且明確指出system class loader載入的"java.lang.Thread"執行個體有記憶體聚集,並建議用關鍵字"java.lang.Thread"進行檢查。所以,MAT通過簡單的兩句話就說明了問題所在,就算使用者沒什麼處理記憶體問題的經驗。在下面還有一個"Details"連結,在點開之前不妨考慮一個問題:為何對象執行個體會聚集在記憶體中,為何存活(而未被GC)?是的——Strong Ref,那麼再走近一些吧。
點擊了"Details"連結之後,除了在上一頁看到的描述外,還有Shortest Paths To the Accumulation Point和Accumulated Objects部分,這裡說明了從GC root到聚集點的最短路徑,以及完整的reference chain。觀察Accumulated Objects部分,java.util.HashMap和java.lang.Object[1000000]執行個體的retained heap(size)最大,在上一篇文章中我們知道retained heap代表從該類執行個體沿著reference chain往下所能收集到的其他類執行個體的shallow heap(size)總和,所以明顯類執行個體都聚集在HashMap和Object數組中了。這裡我們發現一個有趣的現象,既Object數組的shallow heap和retained heap竟然一樣,通過Shallow and retained sizes一文可知,數組的shallow heap和一般對象(非數組)不同,依賴於數組的長度和裡面的元素的類型,對數組求shallow heap,也就是求數組集合內所有對象的shallow heap之和。好,再來看org.rosenjiang.bo.Pilot對象執行個體的shallow heap為何是16,因為對象頭是8位元組,成員變數int是4位元組、String引用是4位元組,故總共16位元組。
在Accumulated Objects視圖中,retained heap佔用最多的是hashMap和object數組,為啥它們會佔用這麼大的heap呢?這個時候需要分析hashMap和object數組中存放了一些什麼對象?接著往下看,來到了Accumulated Objects by Class地區,顧名思義,這裡能找到被聚集的對象執行個體的類名。org.rosenjiang.bo.Pilot類上頭條了,被執行個體化了290,325次,再返回去看程式,我承認是故意這麼乾的。還有很多有用的報告可用來協助分析問題,只是本文中的例子太簡單,也用不上。
為了更多的瞭解MAT的功能,再舉一些例子(不提供對應的代碼):
例子二:
通過MAT發現heap dump問題所在,就需要尋找導致記憶體流失的代碼點。這時往往需要開啟對象相依樹狀結構形視圖,點擊按鈕即可。
這時會看到如下視圖:
這個視圖的右邊大地區可以看到對象的依賴關係,選中某個對象以後可以在左邊小視窗查看對象的一些屬性。如果屬性的值是一些記憶體位址你還可以點擊工具列的搜尋按鈕來搜尋具體的對象資訊。在進行具體分析的時候MAT只是起了協助你進行分析的工具的功能,OOM問題分析沒有固定方法和準則。只能發揮你敏銳的洞察力,結合原始碼,對記憶體中的對象進行分析從而找到代碼中的BUG.
例子三:
如何查看某一個對象佔用的記憶體空間
1.按以下方式開啟新視窗即可 ,
2.輸入類名(輸入類的全名) ,
參考資料:
http://www.blogjava.net/rosen/archive/2010/06/13/323522.html
http://seanhe.iteye.com/blog/898277
效能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)【轉】