前兩天,Square開源了一個內存泄露自動探測神器——LeakCanary,它是一個Android和Java的內存泄露檢測庫,能夠大幅度減小了開發中遇到的OOM問題,對於開發者來講,無疑是個福音,下面對該庫的readme進行簡單的翻譯:html
「A small leak will sink a great ship.」 - Benjamin Franklin
小漏不補沉大船。——本傑明 富蘭克林java
在項目的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
問得好!咱們正好寫了個博客回答這個問題。服務器
使用一個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); } }
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
你能夠在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的菜單。
隨着時間過去愈來愈多熟知的內存泄露問題被製造商在android開源項目中修復。當這樣一個泄露發生時,你能做爲一個應用程序開發員來修復它。出於這個緣由,LeakCanary有一個內置Android泄露的列表AndroidExcludedRefs.java來監測它,若是你找到一個新的泄露,請用leaktrace建立一個issue,標明設備和Android版本。若是你提供一個heap dump的文件連接就更好了。
這是對於新發布的Android版原本說是特別重要的。你有機會更早地幫助檢測新的內存泄露,這有益於整個Android社區。
開發版快照能夠經過Sonatype's snapshots repository找到。
有時leak trace不夠清晰,你須要使用MAT和YourKit深刻研究heap dump。這裏教你怎樣在head dump找到泄露的實例:
1.找出包com.squareup.leakcanary.KeyedWeakReference下全部實例;
2.對於每一個實例,考慮它的key域;
3.找到 KeyedWeakReference 有一個key域等於被LeakCanary報出的引用的key;
4.KeyedWeakReference的referent域是程序中內存泄露的對象;
5.從那時起,問題就轉到你的手上了。一個好的開始是找到最短的GC roots的路徑(排除弱引用)
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>
譯者:cfanr
轉載需說明出處:http://navyifanr.github.io/2015/05/09/LeakCanary-project/