mat之一--eclipse安裝Memory Analyzer


 

工欲善其事必先利其器,先開始更新下eclipse,順便裝下工具軟件。那么簡要的寫寫怎么從頭安裝Memory Analyzer

Memory Analyzer (Eclipse MAT)是一個跨平台的開源工具,不僅可以用它來分析內存問題,也可以用來監控整個 Java 應用程序的狀態和行為。通過讀取應用程序運行時由 Java 運行時環境生成的轉儲文件快照, 能夠分析那些調試代碼可能無法發現的復雜問題。

安裝 MAT

和其他插件的安裝非常類似,MAT 支持兩種安裝方式,一種是“單機版“的,也就是說用戶不必安裝 Eclipse IDE 環境,MAT 作為一個獨立的 Eclipse RCP 應用運行;另一種是”集成版“的,也就是說 MAT 也可以作為 Eclipse IDE 的一部分,和現有的開發平台集成。

集成版的安裝需要借助 Update Manager。通過 Help -> Software Updates... 啟動軟件更新管理向導。

 

下面,開始我們的安裝過程。

首先,打開eclipse->Help->Install new software->work with右邊的Add...

彈出的對話框如下

在Name框中輸入名稱,location中輸入如下地址:

http://download.eclipse.org/mat/1.4/update-site/

這個地址安裝的版本是1.2.1。

具體版本下載地址為http://www.eclipse.org/mat/downloads.php

點擊確定就好。

確定了之后,出現了選擇安裝的界面。選擇全部即可。進行下一步一路next下去即可。

或者從Eclipse Marketplace中安裝

 

安裝完成后,為了更有效率的使用 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中的參數即可。

 

生成分析報告

切換視圖如下,及瀏覽打開Dump文件

概覽

通過上面的概覽,我們對內存占用情況有了一個總體的了解。先檢查一下 MAT 生成的一系列文件

可以看到 MAT 工具提供了一個很貼心的功能,將報告的內容壓縮打包到一個 zip 文件,並把它存放到原始堆轉儲文件的存放目錄下,這樣如果您需要和同事一起分析這個內存問題的話,只需要把這個小小的 zip 包發給他就可以了,不需要把整個堆文件發給他。並且整個報告是一個 HTML 格式的文件,用瀏覽器就可以輕松打開。

我解壓b_Leak_Suspects后,

打開index.html后,如下圖:

接下來我們就可以來看看生成的報告都包括什么內容,能不能幫我們找到問題所在吧。您可以點擊工具欄上的 Leak Suspects 菜單項來生成內存泄露分析報告,也可以直接點擊餅圖下方的 Reports->Leak Suspects 鏈接來生成報告。

分析三步曲

通常我們都會采用下面的“三步曲”來分析內存泄露問題:

首先,對問題發生時刻的系統內存狀態獲取一個整體印象。

第二步,找到最有可能導致內存泄露的元凶,通常也就是消耗內存最多的對象

接下來,進一步去查看這個內存消耗大戶的具體情況,看看是否有什么異常的行為。

下面將用一個基本的例子來展示如何采用“三步曲”來查看生產的分析報告。

查看報告之一:內存消耗的整體狀況

圖 7. 內存泄露分析報告

如圖 7 所示,在報告上最醒目的就是一張簡潔明了的餅圖,從圖上我們可以清晰地看到一個可疑對象消耗了系統 99% 的內存。

在圖的下方還有對這個可疑對象的進一步描述。我們可以看到內存是由 java.util.Vector 的實例消耗的,com.ibm.oti.vm.BootstrapClassLoader 負責這個對象的加載。這段描述非常短,但我相信您已經可以從中找到很多線索了,比如是哪個類占用了絕大多數的內存,它屬於哪個組件等等。

接下來,我們應該進一步去分析問題,為什么一個 Vector 會占據了系統 99% 的內存,誰阻止了垃圾回收機制對它的回收。

查看報告之二:分析問題的所在

首先我們簡單回顧下 JAVA 的內存回收機制,內存空間中垃圾回收的工作由垃圾回收器 (Garbage Collector,GC) 完成的,它的核心思想是:對虛擬機可用內存空間,即堆空間中的對象進行識別,如果對象正在被引用,那么稱其為存活對象,反之,如果對象不再被引用,則為垃圾對象,可以回收其占據的空間,用於再分配。

在垃圾回收機制中有一組元素被稱為根元素集合,它們是一組被虛擬機直接引用的對象,比如,正在運行的線程對象,系統調用棧里面的對象以及被 system class loader 所加載的那些對象。堆空間中的每個對象都是由一個根元素為起點被層層調用的。因此,一個對象還被某一個存活的根元素所引用,就會被認為是存活對象,不能被回收,進行內存釋放。因此,我們可以通過分析一個對象到根元素的引用路徑來分析為什么該對象不能被順利回收。如果說一個對象已經不被任何程序邏輯所需要但是還存在被根元素引用的情況,我們可以說這里存在內存泄露。

現在,讓我們開始真正的尋找內存泄露之旅,點擊“Details ”鏈接,可以看到如圖 8 所示對可疑對象 1 的詳細分析報告。

圖 8. 可疑對象 1 的詳細分析報告

 

  1. 我們查看下從 GC 根元素到內存消耗聚集點的最短路徑:
圖 9. 從根元素到內存消耗聚集點的最短路徑

我們可以很清楚的看到整個引用鏈,內存聚集點是一個擁有大量對象的集合,如果你對代碼比較熟悉的話,相信這些信息應該能給你提供一些找到內存泄露的思路了。

接下來,我們再繼續看看,這個對象集合里到底存放了什么,為什么會消耗掉如此多的內存。

圖 10. 內存消耗聚集對象信息

在這張圖上,我們可以清楚的看到,這個對象集合中保存了大量 Person 對象的引用,就是它導致的內存泄露。

至此,我們已經擁有了足夠的信息去尋找泄露點,回到代碼,我們發現,是下面的代碼導致了內存泄露 :

清單 1. 內存泄漏的代碼段
1
2
3
4
5
6
7
while (1<2)
{
            
     Person person = new Person("name","address",i);
     v.add(person);
     person = null;
}

 

Histogram 查詢

 

用的最多的功能是 Histogram,點擊 Actions下的 Histogram項將得到 Histogram結果:

它按類名將所有的實例對象列出來,可以點擊表頭進行排序,在表的第一行可以輸入正則表達式來匹配結果 :

 

 在某一項上右鍵打開菜單選擇 list objects ->with incoming refs 將列出該類的實例:

它展示了對象間的引用關系,比如展開后的第一個子項表示這個 HomePage(0x420ca5b0)被HomePageContainer(0x420c9e40)中的 mHomePage屬性所引用.

快速找出某個實例沒被釋放的原因,可以右健 Path to GC Roots-->exclue all phantom/weak/soft etc. reference :

得到的結果是:

從表中可以看出 PreferenceManager -> … ->HomePage這條線路就引用着這個 HomePage實例。用這個方法可以快速找到某個對象的 GC Root,一個存在 GC Root的對象是不會被 GC回收掉的.

 

 Histogram 對比

 

為查找內存泄漏,通常需要兩個 Dump結果作對比,打開 Navigator History面板,將兩個表的 Histogram結果都添加到 Compare Basket中去 :

添加好后,打開 Compare Basket面板,得到結果:

點擊右上角的 ! 按鈕,將得到比對結果:

注意,上面這個對比結果不利於查找差異,可以調整對比選項:

再把對比的結果排序,就可得到直觀的對比結果:

也可以對比兩個對象集合,方法與此類似,都是將兩個 Dump結果中的對象集合添加到Compare Basket中去對比。找出差異后用 Histogram查詢的方法找出 GC Root,定位到具體的某個對象上。

 

例子

 

舉例一個典型的分析內存泄漏的過程:

1.  使用 Heap查看當前堆大小為 23.00M

2.  添加一個頁后堆大小變為 23.40M

3.  將添加的一個頁刪除,堆大小為 23.40M

4.  多次操作,結果仍相似,說明添加/刪除頁存在內存泄漏 (也應注意排除其它因素的影響)

5.  Dump 出操作前后的 hprof 文件 (1.hprof,2.hprof),用 mat打開,並得到 histgram結果

6.  使用 HomePage字段過濾 histgram結果,並列出該類的對象實例列表,看到兩個表中的對象集合大小不同,操作后比操作前多出一個 HomePage,說明確實存在泄漏

7.  將兩個列表進行對比,找出多出的一個對象,用查找 GC Root的方法找出是誰串起了這條引用線路,定位結束

PS :

·        很多時候堆增大是 Bitmap引起的,Bitmap在 Histogram中的類型是 byte [],對比兩個 Histogram中的 byte[]對象就可以找出哪些 Bitmap有差異

·        多使用排序功能,對找出差異很有用

 

 

2 內存泄漏的原因分析

 

總結出來只有一條: 存在無效的引用! 
良好的模塊設計以及合理使用設計模式有助於解決此問題。

 

3 Tips

 

·    使用 android:largeHeap="true"標記 (API Level >= 11) 
在 AndroidManifest.xml中的 Application節點中聲明即可分配到更大的堆內存, android:largeHeap標記在 Android系統應用中也有廣泛的應用 ,比如 Launcher, Browser這些內存大戶上均有使用.

 

 

4 參考

 

·    DDMS 官方教程 http://developer.android.com/tools/debugging/ddms.html

·    MAT 下載 http://www.eclipse.org/mat/downloads.php

·    MAT 使用 http://android-developers.blogspot.tw/2011/03/memory-analysis-for-android.html

 同事起草,整理分享,可以轉載收錄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM