一、簡介
MAT是Memory Analyzer tool的縮寫,是一種快速,功能豐富的Java堆分析工具,能幫助你查找內存泄漏和減少內存消耗。很多情況下,我們需要處理測試提供的hprof文件,分析內存相關問題,那么MAT也絕對是不二之選。 Eclipse可以下載插件結合使用,也可以作為一個獨立分析工具使用,下載地址:MAT
二、獲取hprof文件
我們先了解如何獲取hprof文件
在AS monitor 的 memory部分,進行如下操作:

左邊欄選擇Captures,在Heap Snapshot文件夾內會生成對應的hprof文件,再轉化為可供MAT使用的標準hprof文件,操作如下:

如果不用MAT,那么不需要轉為標準hprof文件,在右邊部門就能看到分析報表,可以進行對應分析。但是建議還是使用MAT工具分析,功能更加全面些。
三、MAT工具使用介紹
如何下載和安裝這里就不講了,自行百度或者google。
用工具打開標準.hprof文件后界面如下:

我們主要分析Actions, 它包含了4個部分:
| 視圖 | 含義 |
|---|---|
| histogram | 列舉內存中對象存在的個數(shù)和大小 |
| Dominator tree | 該視圖會以占用總內存的百分比來列舉所有實例對象,注意這個地方是對象而不是類了,這個視圖是用來發(fā)現(xiàn)大內存對象的 |
| Top Consumers | 該視圖會顯示可能的內存泄漏點 |
| Duplicate Classes | 該視圖顯示重復的類等信息 |
點擊他們能得到不同的視圖,下面來一一介紹:
3.1 Histogram:
點擊Histogram之后,會出現(xiàn)如下界面:

這個視圖中提供了多種方式來對對象進行分類,這里為了分析方便,我們選擇按包名進行分類。
下面再來解釋下列名:
| 列名 | 含義 |
|---|---|
| Object | 該類在內存當中的對象個數(shù) |
| Shallow Heap | 對象自身所占用的內存大小,不包括它所引用的對象的內存大小 |
| Retained Heap | 該對象被垃圾回收器回收之后,會釋放的內存大小 |
我們再來看一下右鍵菜單選項:

1)List objects:

with outgoing references: 查看它所引用的對象
with incoming references: 查看它被哪些對象引用
2)Show objects by class
和List objects選項類似,只不過列出的是類名。
3)Merge Shortest Paths to GC Roots
我們可以選擇排除一些類型的引用:

3.2 Dominator Tree:
通過“引用樹”的方式來展現(xiàn)內存的使用情況的,通俗點來說,它是站在對象的角度來觀察內存的使用情況的,主要看是否存在異常的大內存對象
3.3 Top consumers 和 Duplicate Classes: 不常用,讀者可以自行使用下
四、簡單案例分析
案例:將Activity的實例被一個單例對象所持有,在旋轉屏幕的時候造成內存泄漏
public class CommonUtils {
private static CommonUtils instance;
private Context context;
private CommonUtils(Context context){
this.context = context;
}
public static CommonUtils getInstance(Context context){
if(instance == null){
instance = new CommonUtils(context);
}
return instance;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CommonUtils.getInstance(this);//單例模式內存泄漏
}
}
4.1生成hprof文件并導入MAT:
生成過程不贅述了,2份hprof文件 一份是沒有旋轉過屏幕的, 一份是旋轉過屏幕多次的,用來做泄漏和無泄漏的對比,幫助定位問題。
打開MAT 導入我們的2個hprof文件 Open File-->選擇文件-->Leak Suspects Report-->Finish

4.2生成比較結果表
兩個文件都分別先點擊Action中histogram視圖,然后打開下面的面板Navigation History ,會發(fā)現(xiàn)有兩個histogram,分別選中histogram右鍵add to Compare Basket 添加到比較容器中。

添加到比較容器,然后進行比較:

比較之后生成Compared Tables:

然后通過合理的懷疑和猜測,且通過對比,找出有問題的類。當然,這個列子很簡單,而且位置也很清楚,但是較為復雜的項目,這一步就是體力活了。只能耐著性子一點點分析比較。

定位到hasMemLeak.hprof存在內存泄漏:
下一步,找到hasMemLeak.hprof對應的histogram, 再去觀察MainActivtiy具體被哪些對象引用:

看到MainActivty被引用的地方這么多 而且一屏還顯示不完 我們又如何去判斷是哪個導致內存泄漏的呢 MAT還有一個功能 就是通過遍歷GC Root樹去將那些有可能被GC回收的實例 將他們去除(備注:在GC Root樹中能找到的對象絕對不存在有內存泄漏的實例 因為他們在運行時會被回收的嘛 只有找不到的那些才是:

排除虛引用、弱引用、軟引用,之后剩余的才是有分析價值的:

找到問題然后去修改代碼:
CommUtil instance = CommUtil.getInstance(getApplicationContext());
之后再抓hprof測試修改
分析總結:
通過操作,觀察android monitor的內存變化,在橫豎屏切換或者頁面進入退出之后,內存在短時間沒有明顯回落的,都是內存泄漏的懷疑點。確認幾個合理的懷疑范圍之后,通過抓取對比的hprof文件,先定位到泄漏的類,然后再通過分析它引用了誰或者誰引用了它來定位最終問題。同時可以通過排除虛引用、弱引用、軟引用等等GC可以回收的引用來縮小分析范圍。
不得不承認,MAT分析內存問題,還是比較麻煩的,如果想圖方便的話,下期會講一個傻瓜式工具:LeakCanary.