1. 程式人生 > >利用Android Studio、MAT對Android進行記憶體洩漏檢測

利用Android Studio、MAT對Android進行記憶體洩漏檢測

專案進入維護階段時才有時間測試分析app的記憶體問題,這時就要用到測試工具了,可以使用Android Studio、MAT互相結合進行測試,

但是對於複雜的,這兩者很難分析出來,但這兩測試工具也是必須掌握的,感覺網上大多文章講得不怎麼細緻,所以想寫篇文章記錄下,剛好看到本文章,和我想表達的一致

毫不客氣地轉載了;

本文章轉載自 : http://www.cnblogs.com/taoweiji/p/5760537.html

Android開發中難免會遇到各種記憶體洩漏,如果不及時發現處理,會導致出現記憶體越用越大,可能會因為記憶體洩漏導致出現各種奇怪的crash,甚至可能出現因記憶體不足而導致APP崩潰。

記憶體洩漏分析工具

Android的記憶體洩漏分析工具常用有Android Studio和基於eclipse的MAT(Memory Analyzer Tool)。通過兩者配合,可以發揮出奇妙的效果。Android Studio能夠快速定位記憶體洩漏的Activity,MAT能根據已知的Activity快速找出記憶體洩漏的根源。

第一步:強制GC,生成Java Heap檔案

我們都知道Java有一個非常強大的垃圾回收機制,會幫我回收無引用的物件,這些無引用的物件不在我們記憶體洩漏分析的範疇,Android Studio有一個Android Monitors幫助我們進行強制GC,獲取Java Heap

檔案。

強制GC:點選Initate GC(1)按鈕,建議點選後等待幾秒後再次點選,嘗試多次,讓GC更加充分。然後點選Dump Java Heap(2)按鈕,然後等到一段時間,生成有點慢。

生成hprof檔案

生成的Java Heap檔案會在新建視窗開啟。

Analyzer Tasks

第二步:分析記憶體洩漏的Activity

點選Analyzer TasksPerform Analysis(1)按鈕,然後等待幾秒十幾秒不等,即可找出記憶體洩漏的Activity(2)。

Analyzer_Tasks_2

那麼我們就可以知道記憶體洩漏的Activity,因為這個例子比較簡單,其實在(3)就已經可以看到問題所在,如果比較複雜的問題Android Studio並不夠直觀,不夠MAT方便,如果Android Studio無法解決我們的問題,就建議使用MAT來分析,所以下一步我們就生成標準的hprof檔案,通過MAT來找出洩漏的根源。

第三步:轉換成標準的hprof檔案

剛才生成的Heap檔案不是標準的Java Heap,所以MAT無法開啟,我們需要轉換成標準的Java Heap檔案,這個工具Android Studio就有提供,叫做Captures,右擊選中的hprofExport to standard .hprof選擇儲存的位置,即可生成一個標準的hprof檔案。

standard_hprof.png

第四步:MAT開啟hprof檔案

MAT的下載地址,使用方式和eclipse一樣,這裡就不多說了,開啟剛才生成的hprof檔案。點選(1)按鈕開啟Histogram。(2)這裡是支援正則表示式,我們直接輸入Activity名稱,點選enter鍵即可。

Histogram_1

搜尋到了目標的Activity

Histogram_2

右擊搜尋出來的類名,選擇Merge Shortest Paths to GC Rootsexclude all phantom/weak/soft etc. references,來到這一步,就可以看到記憶體洩漏的原因,我們就需要根據記憶體洩漏的資訊集合我們的程式碼去分析原因。

Histogram_3

第六步:根據記憶體洩漏資訊和程式碼分析原因

使用Handler案例分析,給出的資訊是Thread和android.os.Message,這個Thread和Message配合通常是在Handler使用,結合程式碼,所以我猜測是Handler導致記憶體洩漏問題,檢視程式碼,直接就在函式中定義了一個final的Handler用來定時任務,在Activity的onDestroy後,這個Handler還在不斷地工作,導致Activity無法正常回收。

// 導致記憶體洩漏的程式碼
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
textView = (TextView) findViewById(R.id.text);
final Handler handler = new Handler();
handler.post(new Runnable() {
 @Override
 publicvoidrun() {
   textView.setText(String.valueOf(timer++));
   handler.postDelayed(this, 1000);
  }
 });
}
修改程式碼,避免記憶體洩漏
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
textView = (TextView) findViewById(R.id.text);
handler.post(new Runnable() {
 @Override
 publicvoidrun() {
  textView.setText(String.valueOf(timer++));
  if (handler != null) {
   handler.postDelayed(this, 1000);
  }
 }
});
}
private Handler handler = new Handler();
@Override
protectedvoidonDestroy() {
 super.onDestroy();
 // 避免Handler導致記憶體洩漏
 handler.removeCallbacksAndMessages(null);
 handler = null;
}

重新測試,確保問題已經解決。

文章程式碼