使用Eclipse Memory Analyzer Tool(MAT)分析故障

   Eclipse Memory Analyzer Tool(MAT)是一個強大的基於Eclipse的內存分析工具,能夠幫助咱們找到內存泄露,減小內存消耗。html

    工做中常常會遇到一些內存溢出、內存泄露等問題,同時還可能致使CPU使用率也很高,由於在頻繁的進行GC垃圾回收,這時候就須要分析致使問題的緣由,MAT是一個比較好用的工具,但剛開始使用時對於其提供的一些功能仍是不太瞭解,故在此總結一下我的以爲比較有用的一些MAT相關概念,其它功能暫時還未用到或者尚未理解使用方法,後續再補充。java

 

    如下是本文的目錄大綱:數組

    視圖 & 功能瀏覽器

        一、Overviewdom

        二、Histogram視圖工具

        三、Dominator Tree(支配樹)視圖spa

        四、Group分組功能線程

        五、Thread Overview3d

        六、List objectscode

        七、Paths to GC Roots(從對象到GC Roots的路徑) & Merge Shortest Paths to GC roots(從GC Roots到對象的共同路徑)

        八、Leak Suspects Report(內存泄露報告)

 

    如有不正之處請多多諒解,歡迎批評指正、互相討論。

    請尊重做者勞動成果,轉載請標明原文連接:

    http://www.cnblogs.com/trust-freedom/p/6744948.html 

視圖 & 功能

一、Overview

經過 File > Open Heap Dump... 打開dump文件,最早展現就是Overview概述界面,能夠對Heap Dump有一個大體的瞭解,並提供了一些視圖、報告的入口,這些視圖、報告都對分析Heap Dump頗有幫助,後續會介紹。

鼠標移動到餅圖某個區域上方,在左側會看到對象的詳細信息,如左上方的 Inspector 展現瞭如:對象hashcode、類名、包名、Class類對象、父類、類加載器、shallow size、retained size、GC root類型。左下方展現了對象的一些屬性信息、類層級信息。

二、Histogram視圖

如下方式能夠打開Histogram柱狀圖:

(1)點擊Overview頁面Actions區域內的「Histogram視圖」連接

(2)點擊工具欄的「histogram按鈕」

Histogram視圖:

 

該視圖以Class類的維度展現每一個Class類的實例存在的個數、 佔用的 [Shallow內存] 和 [Retained內存] 大小,能夠分別排序顯示。

從Histogram視圖能夠看出,哪一個Class類的對象實例數量比較多,以及佔用的內存比較大,Shallow Heap與Retained Heap的區別會在後面的概念介紹中說明。

不過,多數狀況下,在Histogram視圖看到實例對象數量比較多的類都是一些基礎類型,如char[](由於其構成了String)、String、byte[],因此僅從這些是沒法判斷出具體致使內存泄露的類或者方法的,可使用 List objects 或 Merge Shortest Paths to GC roots 等功能繼續鑽取數據。若是Histogram視圖展現的數量多的實例對象不是基礎類型,是有嫌疑的某個類,如項目代碼中的bean類型,那麼就要重點關注了。

三、Dominator Tree(支配樹)視圖

如下方式能夠打開Dominator Tree視圖:

(1)點擊Overview頁面Actions區域內的「Dominator Tree視圖」連接

(2)點擊工具欄的「Dominator Tree按鈕」 ,爲整個堆打開一個支配樹視圖

Dominator Tree(支配樹)視圖:

 

該視圖以實例對象的維度展現當前堆內存中Retained Heap佔用最大的對象,以及依賴這些對象存活的對象的樹狀結構

視圖中展現了實例對象名、Shallow Heap大小、Retained Heap大小、以及當前對象的Retained Heap在整個堆中的佔比

點開Dominator Tree實例對象左側的「+」,會展現出下一層(next level),當全部引用了當前實例對象的引用都被清除後,下一層列出的objects就會被垃圾回收

這也闡明瞭「支配」的含義:父節點的回收會致使子節點也被回收,即由於父節點的存在使得子節點存活

Dominator Tree支配樹能夠很方便的找出佔用Retained Heap內存最多的幾個對象,並表示出某些objects的是由於哪些objects的緣由而存活,在以後的 Dominator Tree概念 部分會對支配樹作更詳細的說明和舉例

四、Group分組功能

使用Group分組功能的方法是,在 Histogram視圖 和 Domiantor Tree視圖時,點擊工具欄的 Group result by...

能夠選擇以另外一種分組方式顯示(默認是No Grouping(objects),即以對象維度分組)

例如在Histogram視圖 或 Dominator Tree視圖,選擇Group by package,能夠更好地查看具體是哪一個包裏的類佔用內存大,也很容易定位到本身的應用程序

五、Thread Overview

Thread視圖的入口,在工具欄上:

 

Thread Overview:

 

在Thread Overview視圖能夠看到:線程對象/線程棧信息、線程名、Shallow Heap、Retained Heap、類加載器、是否Daemon線程等信息

在分析內存Dump的MAT中還能夠看到線程棧信息,這自己就是一個強大的功能,相似於jstack命令的效果

並且還能結合內存Dump分析,看到線程棧幀中的本地變量,在左下方的對象屬性區域還能看到本地變量的屬性,真的很方便

1
2
3
4
5
6
7
8
9
10
11
12
public  class  TestThreadOverview {
     private  String str1 =  "str1" ;
     private  String str2 =  "str2" ;
 
     public  static  void  main(String[] args) {
         TestThreadOverview test =  new  TestThreadOverview();
         
         String local_str =  "local_str" ;
         
         LockSupport.park();
     }
}

在上面代碼的Heap Dump分析中,能夠看到線程調用棧的信息,以及main線程的 本地變量TestThreadOverview 和 字符串local_str 的信息

   

上圖中第一個框起來的部分是 new TestThreadOverview()對象(代碼第6行),TestThreadOverview對象有兩個屬性str一、str2

第二個框起來的部分是main方法中的字符串變量local_str(代碼第8行)

結合左側的對象屬性區域,能夠更方便的看清線程中對象的具體狀況

六、List objects

在 Histogram 或 Dominator Tree視圖,想要看某個條目(對象/類)的引用關係圖,可使用 List objects 功能

(1)選擇一個條目後,點擊工具欄的 Query Browser > List objects,選擇 with outgoing references 或 with incoming references

(2)直接在某個條目上點擊右鍵,也能夠選擇到List object

 

List objects --> with outgoing references 查看當前對象持有的外部對象引用(在對象關係圖中爲從當前對象指向外的箭頭)

List objects --> with incoming references 查看當前對象被哪些外部對象所引用(在對象關係圖中爲指向當前對象的箭頭)

例如上面Thread Overview的例子代碼中,查看main方法中第6行中的

TestThreadOverview test = new  TestThreadOverview();

outgoing references查詢結果爲:

  

能夠看到TestThreadOverview對象存在3個引用,第一個是TestThreadOverview的Class類對象,由於全部Java類都繼承自java.lang.Object,因此都有class對象的引用,後兩個是成員變量str一、str2

即列出了當前main方法中的局部變量TestThreadOverview所持有的全部外部對象引用

incoming references查詢結果爲:

能夠看到TestThreadOverview是main線程的一個本地局部變量,main線程自己仍是一個GC root,而main線程在某個ThreadGroup中

七、Paths to GC Roots(從對象到GC Roots的路徑) & Merge Shortest Paths to GC roots(從GC Roots到對象的共同路徑)

Paths to GC Roots 從當前對象到GC roots的路徑,這個路徑解釋了爲何當前對象還能存活,對分析內存泄露頗有幫助,這個查詢只能針對單個對象使用

Merge Shortest Paths to GC roots 從GC roots到一個或一組對象的公共路徑

 

Path to GC roots 和 Merge shortest Paths to GC roots 這兩個查詢都有不少選項,如:

 

意思是在查詢到GC root的路徑時,是包含全部引用,仍是排除一些類型的引用(如軟引用、弱引用、虛引用),從GC角度說,一個對象沒法被GC,必定是由於有強引用存在,其它引用類型在GC須要的狀況下都是能夠被GC掉的,因此可使用 exclude all phantom/weak/soft etc. references 只查看GC路徑上的強引用

 

Path to GC roots 和 Merge shortest Paths to GC roots 的入口和 List objects同樣,能夠從工具欄的 Query Browser 進入,或者在條目上直接點擊右鍵進入

須要注意的是,Paths to GC roots是針對單個對象的,故在Histogram視圖沒法使用,由於Histogram視圖是針對類的,只能使用Merge shortest Paths to GC roots查詢

八、Leak Suspects Report(內存泄露報告)

使用MAT打開一個Dump文件時,會彈出嚮導窗口,保持默認選項,點Finish,就會導向 Leak Suspects內存泄露報告頁面

若是打開Dump時跳過了的話,也能夠從其它入口進入,如

(1)工具欄上的 Run Expect System Test > Leak Suspects

(2)Overview頁面的Reports部分

 

Leak Suspects 是MAT幫咱們分析的可能有內存泄露嫌疑的地方,能夠體現出哪些對象被保持在內存中,以及爲何它們沒有被垃圾回收

MAT提供了一個很貼心的功能,將報告的內容壓縮打包到一個zip文件,並放在原始堆轉儲文件的目錄下,通常命名爲「xxx_Leak_Suspects.zip」,xxx是dump文件的名字,若是須要和同事一塊兒分析這個內存問題的話,只須要把這個小小的zip包發給他就能夠了,不須要把整個堆文件發給他。而且整個報告是一個HTML格式的文件,用瀏覽器就能夠輕鬆打開

內存泄露的概念:
一、內存泄露的這些對象是從GC root可達的,從GC root存在通路能夠與其相連
二、這些對象是無用的,即程序之後不會再使用這些對象
至於怎麼定義程序不會再使用的對象,那就要看具體的程序邏輯了,說白了內存泄露就是該回收的內存沒有被回收

 

下面用一個例子分析如何使用Leak Suspects Report內存泄露報告

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public  class  OOMHeapTest {
     public  static  void  main(String[] args){
         oom();
     }
     
     private  static  void  oom(){
         Map<String, OOMBean> map =  new  HashMap<String, OOMBean>();
         Object[] array =  new  Object[ 1000000 ];
         for ( int  i= 0 ; i< 1000000 ; i++){
             String d =  new  Date().toString();
             OOMBean o =  new  OOMBean(d, i);
             map.put(i+ "_oom" , o);
             array[i] = o;
         }
     }
}

上面的代碼中建立了不少OOMBean,並放入了Map和數組中,因爲是強引用,在主線程運行結束前,GC天然不會回收,一直到內存溢出。

在運行前設置一些VM參數:-Xms2m  -Xmx2m  -XX:+HeapDumpOnOutOfMemoryError

以便程序能夠OutOfMemory,並在發生內存溢出時自動生成內存快照

程序運行一下子後,控制檯打印

java_pid10160.hprof 就是內存dump,能夠在OOMHeapTest類所在工程的根目錄下找到

 

Leak Suspects:

MAT工具分析了heap dump後在界面上很是直觀的展現了一個餅圖,該圖深色區域被懷疑有內存泄漏,能夠發現整個heap才6.8M內存,深色區域就佔了92.11%。接下來是一個簡短的描述,告訴咱們main線程佔用了大量內存,而且明確指出system class loader加載的「java.lang.Thread」實例有內存彙集,並建議用關鍵字「java.lang.Thread」進行檢查。在下面還有一個「Details」連接,能夠查看明細信息。

Details明細:

Details的最開始是Description描述,和前一個頁面對內存泄露嫌疑點的描述一致,下面有一些與懷疑的內存泄露點關聯的查詢結果展現,是分析報告中認爲可能會存在問題,協助咱們深刻分析問題根源的,具體以下:

(1)Shortest Paths To the Accumulation Point

 

實際上展開的視圖是當前對象「java.lang.Thread @ 0xffc59ab0 main」的 Path to GC roots,即到GC roots的路徑,點擊標題右側的按鈕能夠在另外一窗口打開

這個視圖的做用是能夠分析是因爲和哪一個GC root相連致使當前Retained Heap佔用至關大的對象沒法被回收

因爲是分析內存泄露的報告,找到致使當前對象沒法被回收的GC roots,分析這些GC roots是否合理,是有必要的

但本例中因爲main線程自己就是GC root,故只有一條數據

(2)Accumulated Objects in Dominator Tree

這個視圖以對象的維度展現了以當前對象「java.lang.Thread @ 0xffc59ab0 main」爲根的 Dominator Tree支配樹,能夠方便的看出受當前對象「支配」的對象中哪一個佔用Retained Heap比較大

觀察Accumulated Objects部分,java.lang.Object[1000000]實例 和 java.util.HashMap 和 的Retained Heap(Size)最大,Retained Heap表明從該類實例沿着reference chain往下所能收集到的其餘類實例的Shallow Heap(Size)總和,因此明顯類實例都彙集在HashMap和Object數組中了

在Accumulated Objects視圖中,Retained heap佔用最多的是HashMap和object數組,爲啥它們會佔用這麼大的heap呢?這個時候須要分析HashMap和object數組中存放了一些什麼對象?接着往下看 Accumulated Objects by Class in Dominator Tree

(3)Accumulated Objects by Class in Dominator Tree

 

這個視圖其實是展現了以當前對象「java.lang.Thread @ 0xffc59ab0 main」爲根的 Dominator Tree支配樹,並以Class類分組

能夠看到 OOMBean類 的實例最多,有11786個,程序中確實是在循環建立OOMBean實例,並放入object數據和HashMap中

這樣就能夠肯定Heap佔用大時因爲OOMBean類的實例建立的太多的緣由了

(4)Thread Detail

Detail明細的最後因爲當前懷疑泄露點爲main Thread線程對象,故展現了線程明細信息,調用棧信息,對分析內存溢出的發生位置頗有幫忙 

相關文章
相關標籤/搜索