使用android studio檢測app內存泄漏【轉載】

Android開發中不免會遇到各類內存泄漏,若是不及時發現處理,會致使出現內存越用越大,可能會由於內存泄漏致使出現各類奇怪的crash,甚至可能出現因內存不足而致使APP崩潰。android

通常檢測android內存泄漏都是使用android studio自帶的Monitor工具結合mat,或者是使用第三方開源庫工具:leakCanary.正則表達式

可是這二者都不是十分完美的方式:

Monitor工具結合mat有以下的問題:eclipse

  1.   不保存過去的數據,若是須要幾個小時乃至幾天的數據,這個工具就作不到;
  2.   有些內存泄露很慢,可能須要累計幾天纔有明顯的增加,用自帶的工具就不容易發現;

leakCanary有以下的問題:ide

  1. 工具比較死板,有些程序邏輯錯誤並不能檢測出來.
  2. 另外有些內存能夠優化的地方,使用leakcanary也是分析不出來的,好比程序中用到集合類,若是用完不調用clear並置爲null的話,致使一個遊離的對象集合會在內存中存在一段時間,只有等到下次GC檢測到對象沒有用的時候纔會回收。

內存泄漏分析工具

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)按鈕,而後等到一段時間,生成有點慢。post

生成hprof文件

生成的Java Heap文件會在新建窗口打開。測試

Java Heap查看,圈出Analyzer Tasks按鈕

第二步:分析內存泄漏的Activity

點擊Analyzer TasksPerform Analysis(1)按鈕,而後等待幾秒十幾秒不等,便可找出內存泄漏的Activity(2)。優化

Analyzer Tasks,圈出Perform Analysis按鈕,Analysis Results

那麼咱們就能夠知道內存泄漏的Activity,由於這個例子比較簡單,其實在(3)就已經能夠看到問題所在,若是比較複雜的問題Android Studio並不夠直觀,不夠MAT方便,若是Android Studio沒法解決咱們的問題,就建議使用MAT來分析,因此下一步咱們就生成標準的hprof文件,經過MAT來找出泄漏的根源。this

第三步:轉換成標準的hprof文件

剛纔生成的Heap文件不是標準的Java Heap,因此MAT沒法打開,咱們須要轉換成標準的Java Heap文件,這個工具Android Studio就有提供,叫作Captures,右擊選中的hprofExport to standard .hprof選擇保存的位置,便可生成一個標準的hprof文件。

第四步:MAT打開hprof文件

MAT的下載地址,使用方式和eclipse同樣,這裏就很少說了,打開剛纔生成的hprof文件。點擊(1)按鈕打開Histogram。(2)這裏是支持正則表達式,咱們直接輸入Activity名稱,點擊enter鍵便可。

搜索

搜索到了目標的Activity

右擊搜索出來的類名,選擇Merge Shortest Paths to GC Rootsexclude all phantom/weak/soft etc. references,來到這一步,就能夠看到內存泄漏的緣由,咱們就須要根據內存泄漏的信息集合咱們的代碼去分析緣由。

第六步:根據內存泄漏信息和代碼分析緣由

使用Handler案例分析,給出的信息是Thread和android.os.Message,這個Thread和Message配合一般是在Handler使用,結合代碼,因此我猜想是Handler致使內存泄漏問題,查看代碼,直接就在函數中定義了一個final的Handler用來定時任務,在Activity的onDestroy後,這個Handler還在不斷地工做,致使Activity沒法正常回收。

// 致使內存泄漏的代碼protected void onCreate(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 public void run() {   textView.setText(String.valueOf(timer++));   handler.postDelayed(this, 1000);  } });}
修改代碼,避免內存泄漏
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);textView = (TextView) findViewById(R.id.text);handler.post(new Runnable() { @Override public void run() {  textView.setText(String.valueOf(timer++));  if (handler != null) {   handler.postDelayed(this, 1000);  } }});}private Handler handler = new Handler();@Overrideprotected void onDestroy() { super.onDestroy(); // 避免Handler致使內存泄漏 handler.removeCallbacksAndMessages(null); handler = null;}

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

相關文章
相關標籤/搜索