LeakCanary開源項目

前兩天,Square開源了一個內存泄露自動探測神器——LeakCanary,它是一個Android和Java的內存泄露檢測庫,能夠大幅度減小了開發中遇到的OOM問題,對於開發者來講,無疑是個福音,下面對該庫的readme進行簡單的翻譯:html

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

Getting started

在項目的build.gradle文件添加:android

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
 }

在Application類添加:git

public class ExampleApplication extends Application {

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

當在你的debug構建過程當中出現內存泄露時,LeakCanary將會自動展現一個通知欄。github

爲何我應該使用LeakCanary?

問得好!咱們正好寫了個博客回答這個問題。服務器

那怎麼使用它呢?

使用一個RefWatcher觀察引用何時應該被GC:app

RefWatcher refWatcher = {...};

// We expect schrodingerCat to be gone soon (or not), let's watch it.
refWatcher.watch(schrodingerCat);

LeakCanary.install() 返回一個先前配置的RefWatcher,它也安裝一個ActivityRefWatcher以便在Activity.onDestroy()被調用後自動檢測Activity是否出現泄露。eclipse

public class ExampleApplication extends Application {

  public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (ExampleApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

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

你可使用RefWatcher觀察Fragment的內存泄露ide

public abstract class BaseFragment extends Fragment {

  @Override public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
  }
}

How does it work?

1.RefWatcher.watch()建立一個KeyedWeakReference去檢測對象;
2.接着,在後臺線程,它將會檢查是否有引用在不是GC觸發的狀況下須要被清除的;
3.若是引用引用仍然沒有被清除,將會轉儲堆到.hprof文件到系統文件中(it them dumps the heap into a .hprof file stored on the app file system.)
4.HeapAnalyzerService是在一個分離的進程中開始的,HeapAnalyzer經過使用HAHA解析heap dump;
5.因爲一個特殊的引用key和定位的泄露引用,HeapAnalyzer能夠在heap dump中找到KeyedWeakReference;
6.若是有一個泄露,HeapAnalyzer計算到GC Roots的最短的強引用路徑,而後建立形成泄露的引用鏈;
7.結果在app的進程中傳回到DisplayLeakService,並展現泄露的通知消息;gradle

怎樣拷貝leak trace?

你能夠在Logcat上看leak trace:

In com.example.leakcanary:1.0:1 com.example.leakcanary.MainActivity has leaked:
* GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1')
* references com.example.leakcanary.MainActivity$3.this$0 (anonymous class extends android.os.AsyncTask)
* leaks com.example.leakcanary.MainActivity instance

* Reference Key: e71f3bf5-d786-4145-8539-584afaecad1d
* Device: Genymotion generic Google Nexus 6 - 5.1.0 - API 22 - 1440x2560 vbox86p
* Android Version: 5.1 API: 22
* Durations: watch=5086ms, gc=110ms, heap dump=435ms, analysis=2086ms

你也能夠分享leak trace和heap dump文件經過action bar的菜單。

My leak is caused by the SDK implementation!

隨着時間過去愈來愈多熟知的內存泄露問題被製造商在android開源項目中修復。當這樣一個泄露發生時,你能做爲一個應用程序開發員來修復它。出於這個緣由,LeakCanary有一個內置Android泄露的列表AndroidExcludedRefs.java來監測它,若是你找到一個新的泄露,請用leaktrace建立一個issue,標明設備和Android版本。若是你提供一個heap dump的文件連接就更好了。

這是對於新發布的Android版原本說是特別重要的。你有機會更早地幫助檢測新的內存泄露,這有益於整個Android社區。
開發版快照能夠經過Sonatype's snapshots repository找到。

Beyond the leak trace

有時leak trace不夠清晰,你須要使用MATYourKit深刻研究heap dump。這裏教你怎樣在head dump找到泄露的實例:

1.找出包com.squareup.leakcanary.KeyedWeakReference下全部實例;
2.對於每一個實例,考慮它的key域;
3.找到 KeyedWeakReference 有一個key域等於被LeakCanary報出的引用的key;
4.KeyedWeakReference的referent域是程序中內存泄露的對象;
5.從那時起,問題就轉到你的手上了。一個好的開始是找到最短的GC roots的路徑(排除弱引用)

自定義Customizing

圖標和標籤 Icon and label

DisplayLeakActivity自帶一個默認的icon和label,能夠經過提供的R.drawable.__leak_canary_icon和R.string.__leak_canary_display_activity_label來修改:

res/
  drawable-hdpi/
    __leak_canary_icon.png
  drawable-mdpi/
    __leak_canary_icon.png
  drawable-xhdpi/
    __leak_canary_icon.png
  drawable-xxhdpi/
    __leak_canary_icon.png
  drawable-xxxhdpi/
    __leak_canary_icon.png
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="__leak_canary_display_activity_label">MyLeaks</string>
</resources>

<pre><code><br />###儲存leak traces
DisplayLeakActivity能夠在你的app目錄保存7個heap dumps和leak traces,你能夠在app中經過提供R.integer.__leak_canary_max_stored_leaks的值改變這個數量:
</code></pre>

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <integer name="__leak_canary_max_stored_leaks">20</integer>
</resources>

<pre><code><br />###上傳到服務器
你能夠改變默認的行爲去上傳leak trace並heap dump到你選擇的服務器。
建立你本身的AbstractAnalysisResultService,最容易的方式是在你debug的源碼中繼承DefaultAnalysisResultService:

</code></pre>

public class LeakUploadService extends DefaultAnalysisResultService {
  @Override protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
    if (!result.leakFound || result.excludedLeak) {
      return;
    }
    myServer.uploadLeakBlocking(heapDump.heapDumpFile, leakInfo);
  }
}

<pre><code>肯定在你正式發佈的Application類中使RefWatcher失效:
</code></pre>

public class ExampleApplication extends Application {

  public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (ExampleApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    refWatcher = installLeakCanary();
  }

  protected RefWatcher installLeakCanary() {
    return RefWatcher.DISABLED;
  }
}

<pre><code><br />在你的debug的Application類建立一個定製的RefWatcher:
</code></pre>

public class DebugExampleApplication extends ExampleApplication {
  protected RefWatcher installLeakCanary() {
    return LeakCanary.install(app, LeakUploadService.class);
  }
}

<pre><code>不要忘記了在你debug的manifest中註冊service:
</code></pre>

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >
  <application android:name="com.example.DebugExampleApplication">
    <service android:name="com.example.LeakUploadService" />
  </application>
</manifest>

相關閱讀:內存泄露自動探測神器——LeakCanary

譯者:cfanr
轉載需說明出處:http://navyifanr.github.io/2015/05/09/LeakCanary-project/

相關文章
相關標籤/搜索