1. 程式人生 > >LeakCanary:檢測Android中的記憶體洩漏

LeakCanary:檢測Android中的記憶體洩漏

Square開源了一個記憶體洩露自動探測神器——LeakCanary,它是一個Android和Java的記憶體洩露檢測庫,可以大幅度減少了開發中遇到的OOM問題,對於開發者來說,無疑是個福音,下面對該庫的使用進行簡單的介紹。

一 什麼是記憶體洩漏:

有些物件只有有限的生命週期。當它們的任務完成之後,它們將被垃圾回收。如果在物件的生命週期本該結束的時候,這個物件還被一系列的引用,這就會導致記憶體洩漏。隨著洩漏的累積,app將消耗完記憶體。

比如,在Activity.onDestroy()被呼叫之後,view樹以及相關的bitmap都應該被垃圾回收。如果一個正在執行的後臺執行緒繼續持有這個Activity的引用,那麼相關的記憶體將不會被回收,這最終將導致OutOfMemoryError崩潰。

二 LeakCanary介紹:

一個用於檢測Android&Java的記憶體洩漏檢測庫

A memory leak detection library for Android and Java.

“A small leak will sink a great ship.” - Benjamin Franklin
小漏不補沉大船。——本傑明 富蘭克林

三 DEMO使用:

(1)在build.gradle中新增依賴:

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' }

(2)在Application中初始化Leak:

public class MyApp extends Application {
    private static MyApp instance;

    @Override
    public void onCreate
() { super.onCreate(); instance = this; LeakCanary.install(this);//初始化LeakCanary } public static final MyApp getInstance() { return instance; } @Override public void onTerminate() { super.onTerminate(); } }

或者這樣初始化:

public class MyApplication extends Application {
//在自己的Application中新增如下程式碼
  public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (MyApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    refWatcher = LeakCanary.install(this);
  }
}

(3)在Activity中製造一個記憶體洩漏的場景:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    public static MainActivity instance = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        instance = this;//建立一個靜態物件instance,保持其對當前activity的引用;
        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, SecondActivity.class);
                MainActivity.this.startActivity(intent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //instance=null;   //當要銷燬activity的時候,instance並未置空,仍然保持對MainActivity的引用,因此MainActivity不會銷燬;
    }
}

SecondActivity.java:

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MainActivity.instance.finish();//通過instance關閉MainActivity;
            }
        });
    }
}

在finish的時候會出現記憶體洩漏,instance仍然保持對activity的引用。

(4)執行結果如下:

這裡寫圖片描述

然後點選桌面的Leaks圖示可以看到:

這裡寫圖片描述

Leaks指出了記憶體洩漏的位置是instance。

(5)LeakCanary的檢測工作機制:

LeakCanary檢測記憶體洩漏的原理大概分為7個步驟,從建立被關聯物件的弱引用、檢查該應用是否被清除等。

1、建立:RefWatcher.watch() 建立一個 KeyedWeakReference 到要被監控的物件。
2、檢查:然後在後臺執行緒檢查引用是否被清除,如果沒有,呼叫GC。
3、存放:如果引用還是未被清除,把 heap 記憶體 dump 到 APP 對應的檔案系統中的一個 .hprof 檔案中。
4、解析:在另外一個程序中的 HeapAnalyzerService 有一個 HeapAnalyzer 使用HAHA 解析這個檔案。
5、定位:得益於唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位記憶體洩露。
6、計算:HeapAnalyzer 計算 到 GC roots 的最短強引用路徑,並確定是否是洩露。如果是的話,建立導致洩露的引用鏈。
7、展示:引用鏈傳遞到 APP 程序中的 DisplayLeakService, 並以通知的形式展示出來。

圖形路程圖如下所示:

這裡寫圖片描述

參考致謝: