天天看點

android leakcanary 源碼分析,LeakCanary源碼淺析

在Android開發中最讓人們頭疼的就是記憶體洩漏了,今天來介紹一個檢視記憶體是否洩漏的工具LeakCanary,并通過研究源碼明白它是如何分析和查找存在洩漏資訊的

在Android開發中最讓人們頭疼的就是記憶體洩漏了,今天來介紹一個檢視記憶體是否洩漏的工具LeakCanary,并通過研究源碼明白它是如何分析和查找存在洩漏資訊的

首先送上LeakCanary文檔連結:LeakCanary中文使用說明

Part1. 知識回顧常用工具Mat

LeakCanary(Square)

原理:watch監視一個即将要銷毀的對象記憶體種類1、棧(stack-基本資料類型,對象的引用)

2、堆(heap-存放new出來的對象和數組,在堆中配置設定記憶體由GC管理)

3、方法區(method,大體和堆一樣)為什麼會産生記憶體洩漏當一個對象已經不需要再使用了,在該對象被回收時候,有另外的對象引用該回收對象,導緻本該被回收的對象無法回收

有些對象隻有有限的生命周期,當生命周期很短的完成任務後,在本該結束的生命周期中仍然被引用記憶體洩漏會導緻什麼問題OOM常見的記憶體洩漏情況單例造成的記憶體洩漏

非靜态内部類建立靜态執行個體造成的記憶體洩漏

handler造成記憶體洩漏(handler、message、MessageQueue)

解決方法:①将Handler聲明為靜态類型

②通過弱引用的方式引入Activity

線程造成的記憶體洩漏(解決方法:将線程定義聲明為static類型)

webview造成的記憶體洩漏(example:加載頁面很複雜,Ex:大量的圖檔)

Part2 概念引用類型強引用(StrongReference),預設對象一般為強引用

軟引用(SoftReference),當記憶體空間足夠大時相當于強引用,記憶體不夠時通過垃圾回收器(GC)自動回收

弱引用(WeakReference),當GC掃描到該類型的引用時就自己回收

虛引用,相當于沒有進行引用,GC可随時回收該類型的引用ReferenceQueue軟引用和弱引用都持有該對象

對象被垃圾回收,Java虛拟機就會把這個引用加入到與之相關聯的引用隊列中

Part3.LeakCanary使用在module層級中的build.gradle中加入引用,不同的編譯使用不同的引用dependencies {

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'

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

2.在Application中:public class MyApplication extends Application {    @Override

public void onCreate() {        super.onCreate();

LeakCanary.install(this);

}

}

3.在Manifest.xml中加載該Application檔案

Part4. LeakCanary源碼剖析

從代碼入口剖析:LeakCanary.install(this);

跟蹤源碼可知

public static RefWatcher install(Application application) {    return install(application, DisplayLeakService.class);

}

從上面的代碼我們發現這個方法最終傳回給我們一個RefWatcher這個類,這個類是主要是啟動ActivityRefWatcher類,ActivityRefWatcher在Activity的onDestory方法結束時檢測記憶體洩漏。

看下install這個方法:

public static RefWatcher install(Application application,      Class extends AbstractAnalysisResultService> listenerServiceClass) {    if (isInAnalyzerProcess(application)) {      return RefWatcher.DISABLED;

}

enableDisplayLeakActivity(application);

HeapDump.Listener heapDumpListener =        new ServiceHeapDumpListener(application, listenerServiceClass);

RefWatcher refWatcher = androidWatcher(application, heapDumpListener);

ActivityRefWatcher.installOnIcsPlus(application, refWatcher);    return refWatcher;

}

通過RefWatcher refWatcher = androidWatcher(application, heapDumpListener)建立一個RefWatcher對象,啟動activityRefWatcher來監視記憶體洩漏

enableDisplayLeakActivity(application)主要作用是開啟DisplayLeakActivity這個類,這個類主要是顯示記憶體洩漏的彈框頁面ActivityRefWatcher.installOnIcsPlus(application, refWatcher);public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {    if (SDK_INT 

return;

}

ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);

activityRefWatcher.watchActivities();

} public void watchActivities() {    // Make sure you don't get installed twice.

stopWatchingActivities();

application.registerActivityLifecycleCallbacks(lifecycleCallbacks);

}  public void stopWatchingActivities() {

application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);

}

小結:

①通過stopWatcher方法反注冊以前的Activity的生命周期的callback,目的是為了保證以前的記憶體洩漏的activity删除

②重新注冊activity生命周期的callback

③通過lifecycleCallbacks中的onActivityDestroyed方法将activity的生命周期和ActivityReference關聯起來@Override

public void onActivityDestroyed(Activity activity) {

ActivityRefWatcher.this.onActivityDestroyed(activity);

}

跟蹤onActivityDestroyed方法發現通過調用RefWatcher調用了watcher類void onActivityDestroyed(Activity activity) {

refWatcher.watch(activity);

}private final RefWatcher refWatcher

下面我們進入RefWatcher類中發現有如下的變量資訊private final Executor watchExecutor;  private final DebuggerControl debuggerControl;  private final GcTrigger gcTrigger;  private final HeapDumper heapDumper;  private final Set retainedKeys;  private final ReferenceQueue queue;  private final HeapDump.Listener heapdumpListener;

上述變量大意如下:watchExecutor主要用于執行記憶體洩漏檢測

debuggerControl查詢我們是否正在調試中,如果我們正在調試過程中則不會進行判斷

gcTrigger用于處理GC,用于在判斷洩漏對象之前再調用GC類中的方法再次判斷

heapDumper用于dump中記憶體洩漏堆檔案

retainedKeys該set集合持有待檢測和已産生記憶體洩漏資訊的key

queue引用對象,主要是判斷弱引用所持有的對象是否已執行GC垃圾收回

heapdumpListener主要用于分析産生hprof檔案回調

檢視watch方法可知:

public void watch(Object watchedReference, String referenceName) {

checkNotNull(watchedReference, "watchedReference");

checkNotNull(referenceName, "referenceName");    if (debuggerControl.isDebuggerAttached()) {      return;

}    final long watchStartNanoTime = System.nanoTime();

String key = UUID.randomUUID().toString();

retainedKeys.add(key);    final KeyedWeakReference reference =        new KeyedWeakReference(watchedReference, key, referenceName, queue);

watchExecutor.execute(new Runnable() {      @Override public void run() {

ensureGone(reference, watchStartNanoTime);

}

});

}

通過産生一個唯一的key添加到retainedKeys集合中String key = UUID.randomUUID().toString();

retainedKeys.add(key);

再建立一個KeyedWeakReference的弱引用,并開啟一個異步線程來分析建立好的弱引用,該線程主要作用是確定我們的Activity是否真正已經進入到GONE狀态void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {    long gcStartNanoTime = System.nanoTime();

//計算過去的方法到調用GC垃圾收回的時間值

long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);    //清除已經到達我們引用隊列的弱引用

removeWeaklyReachableReferences();    //判斷如果處于debug狀态就不再進行記憶體分析

if (gone(reference) || debuggerControl.isDebuggerAttached()) {      return;

}

gcTrigger.runGc();//手動進行垃圾回收

removeWeaklyReachableReferences();    if (!gone(reference)) {      long startDumpHeap = System.nanoTime();      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

File heapDumpFile = heapDumper.dumpHeap();//dump出記憶體洩漏的檔案

if (heapDumpFile == null) {        // Could not dump the heap, abort.

return;

}      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);      //開始分析記憶體洩漏檔案查找記憶體洩漏路徑

heapdumpListener.analyze(          new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,

heapDumpDurationMs));

}

}

以上代碼部分總結如下:首先建立一個RefWatcher,啟動一個ActivityRefWatcher

通過ActivityLifecyclecallback将Activity的onDestroy生命周期給關聯起來

最後通過執行execute線程來分析洩漏資訊探讨LeakCanary中Activity洩漏檢測機制代碼

在上面的ensureGone方法中最後我們發現有這樣的代碼heapdumpListener.analyze(          new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,

heapDumpDurationMs));

}

通過跟蹤發現analyze方法該方法是HeapDump類中的一個interface接口,再檢視它的實作類發現在ServiceHeapDumpListener這個類中的方法@Override public void analyze(HeapDump heapDump) {

checkNotNull(heapDump, "heapDump");

HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);

}

繼續跟蹤runAnalysis方法發現在HeapAnalyzerService中,且該類繼承了intentService,是以它将會每次調用onHandleIntent方法@Override

protected void onHandleIntent(Intent intent) {

String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);

HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);

AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);

}

通過checkForLeak方法來分析記憶體洩漏資訊的結果,并通過sendResultToListener顯示最終的結果public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {    long analysisStartNanoTime = System.nanoTime();    if (!heapDumpFile.exists()) {

Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);      return failure(exception, since(analysisStartNanoTime));

}

ISnapshot snapshot = null;    try {

snapshot = openSnapshot(heapDumpFile);//生成記憶體快照資訊

IObject leakingRef = findLeakingReference(referenceKey, snapshot);//檢視記憶體的引用

// False alarm, weak reference was cleared in between key check and heap dump.

if (leakingRef == null) {        return noLeak(since(analysisStartNanoTime));

}

String className = leakingRef.getClazz().getName();

AnalysisResult result =

findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, true);      if (!result.leakFound) {

result = findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, false);//尋找記憶體洩漏的路徑

}      return result;

} catch (SnapshotException e) {      return failure(e, since(analysisStartNanoTime));

} finally {

cleanup(heapDumpFile, snapshot);

}

總結checkForLeak方法

1.把.hprof轉為 Snapshotsnapshot = openSnapshot(heapDumpFile);

2.找出洩漏的對象/洩漏對象的最短路徑IObject leakingRef = findLeakingReference(referenceKey, snapshot);

AnalysisResult result =

findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, true);

findLeakingReference作用①在snapshot快照中找到第一個弱引用即為記憶體發生洩漏的引用

②周遊這個對象的所有執行個體資訊

③如果發現存在key值與之前定義封裝好的key值相同,那麼傳回這個定位到的洩漏對象

findLeakTrace是通過擷取記憶體洩漏的引用來擷取洩漏路徑的最短路徑了解LeakCanary的原理Activity Destroy()之後将它放在一個WeakReference中

将WeakReference關聯到一個ReferenceQueue

檢視ReferenceQueue是否存有Activity的引用

如果該Activity洩露了,Dump出heap資訊,然後去分析洩漏路徑