1. LeakCanary是幫忙我們查詢內存泄露的,只需要簡單的繼承,當發生內存泄露時,就會在手機的桌面上生成內存泄露的文件,.hprof
文件。
打開文件大概如下圖,幫助我們分析內存泄露的原因
2. 原理
通過registerActivityLifecycleCallbacks 檢測Activity的生命週期。 在Activity執行onDestroy時,開始檢測。
檢測之前,介紹一下LeakCanary的一個核心的類。
KeyedWeakReference。它是弱引用的子類。我們看一下它的構造函數
參數Object refernce: 是這個弱引用指向的對象。
參數ReferenceQueue<Object> referenceQueue: 這個是是一個隊列,如果弱引用指向的對象被GC回收,那麼對象就會被添加到此隊列中。 所以我們可以判斷,如果一個對象出現在了referenceQueue中,我們就可以認爲它已經被GC回收,不存在內存泄露。
回到在Activity執行onDestroy時,開始檢測。
//生成一個隨機的Key,和Activity對應
String key = UUID.randomUUID().toString();
//把這個key添加到retainedKeys,retainedKeys是還沒有被GC回收的activity的Key的集合
this.retainedKeys.add(key);
// 生成一個弱引用KeyedWeakReference指向我的要destory的activity
//watchedReference 是要destory的activity.
//傳入queue,用來收集稍後被GC回收的activity對象
final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, this.queue);
// 向handler發送一個ensureGone操作,注意這是一個Idle類型的只有handler空閒纔會執行,所以不會阻塞正常的操作。
this.watchExecutor.execute(new Runnable() {
public void run() {
RefWatcher.this.ensureGone(reference, watchStartNanoTime);
}
});
***************準備工作做好了,等待handler空閒執行ensureGone方法******************
ensureGone的時候,handler已經忙完了,正常來說onDestory已經都執行結束了,我們的Activity正常來說應該被回收了。
從queue中不斷poll出對象,獲取到對應的Key, 並從retainedKeys刪除對應的key.
因爲queue中的對象被GC回收,所以把他從retainedKeys除去
queue爲null後,判斷ensureGone傳入的reference的key是否已經不存在在retainedKeys中了。
如果不存在證明確實被GC回收。不用管了,這個activity沒發生內存泄露
如果還在,那麼 this.gcTrigger.runGc(); 再執行一次GC操作。在從queue中不斷poll出對象,獲取到對應的Key, 並從retainedKeys刪除對應的key.....判斷ensureGone傳入的reference的key是否已經不存在在retainedKeys中了。
如果retainedKeys找不到對應的key,說明確實被GC回收。不用管了,這個activity沒發生內存泄露
如果還存在在retainedKeys說明Activity發生內存泄露。
File heapDumpFile = this.heapDumper.dumpHeap(); dump出內存文件。由於這個過程比較耗時,啓動 HeapAnalyzerService ,HeapAnalyzerService通過調用HeapAnalyzer的checkForLeak方法進行內存分析。最終用到
eclipse mat 進行分析,打印內存泄露的路徑