LeakCanary流程介紹分析
LeakCanary,在開發階段,可以用來檢測記憶體洩露,專案地址: ofollow,noindex">https://github.com/square/leakcanary

1.png

2.png
具體操作:1、在Application 的onCreate()初始化,會ActivityLifecycleCallbacks監聽所有的activity的onDestroy,
2、把activity生成對應key的放到集合Set<String> retainedKeys,並生成帶Key的弱引用,GC在回收的時候,如果是弱引用,會把這個回收物件放到ReferenceQueue<T> queue,如果poll出來有物件,說明這個activity物件已回收,移除集合包含,若沒有,判斷集合是否包含key,若有,則就懷疑是洩漏了,需要二次確認,進行手動Runtime.getRuntime().gc(),
3、洩露物件,生成HPROF檔案,分析這個快照檔案有沒有存在帶這個key值的洩漏物件,如果沒有,那麼沒有洩漏,
否則找出最短路徑,列印給我們,就找到這個洩漏物件了。
一、初始化LeakCanary
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); } } public final class LeakCanary { public static RefWatcher install(Application application) { return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); } }
refWatcher()生成AndroidRefWatcherBuilder類,.buildAnInstall(),會生成RefWatcher,並把相應的物件如(GcTrigger、AndroidWatchExecutor、AndroidHeapDumper、ServiceHeapDumpListener、AndroidExcludedRefs等)。
且Application.registerActivityLifecycleCallbacks這個方法,用來統一管理所有activity的生命週期,LeakCanary就是在onDestory()方法實現監控的.
public final class ActivityRefWatcher { ... public static void install(Application application, RefWatcher refWatcher) { new ActivityRefWatcher(application, refWatcher).watchActivities(); } private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { ... @Override public void onActivityDestroyed(Activity activity) { ActivityRefWatcher.this.onActivityDestroyed(activity); } }; private final Application application; private final RefWatcher refWatcher; void onActivityDestroyed(Activity activity) { refWatcher.watch(activity); } public void watchActivities() { stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); } }
二、洩露判斷
在RefWatcher中watch函式中:
public final class RefWatcher { private final WatchExecutor watchExecutor; private final DebuggerControl debuggerControl; private final GcTrigger gcTrigger; private final HeapDumper heapDumper; private final Set<String> retainedKeys; private final ReferenceQueue<Object> queue; private final HeapDump.Listener heapdumpListener; private final ExcludedRefs excludedRefs; public void watch(Object watchedReference, String referenceName) { ... final long watchStartNanoTime = System.nanoTime(); String key = UUID.randomUUID().toString(); retainedKeys.add(key); final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); ensureGoneAsync(watchStartNanoTime, reference); } private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) { watchExecutor.execute(new Retryable() { @Override public Retryable.Result run() { return ensureGone(reference, watchStartNanoTime); } }); }
其中retainedKeys是用來儲存key的集合,KeyedWeakReference是帶有key的弱引用物件,queue是ReferenceQueue,用來GC時候,如果弱引用的物件被回收了, 系統會把這個回收物件放到queue裡面。這樣就可以判斷acitivity是否洩漏了。
private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key); } private void removeWeaklyReachableReferences() { KeyedWeakReference ref; while ((ref = (KeyedWeakReference) queue.poll()) != null) { retainedKeys.remove(ref.key); }
public final class AndroidWatchExecutor implements WatchExecutor { ... @Override public void execute(Retryable retryable) { if (Looper.getMainLooper().getThread() == Thread.currentThread()) { waitForIdle(retryable, 0); } else { postWaitForIdle(retryable, 0); } } void waitForIdle(final Retryable retryable, final int failedAttempts) { Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { postToBackgroundWithDelay(retryable, failedAttempts); return false; } }); } }
watchExecutor是AndroidWatchExecutor,在waitForIdle中有一個Looper.myQueue().addIdleHandler,這個是在當前執行緒中取出MessageQueue物件,新增一個idleHandler,在Looper,loop()迴圈取出Message時,如果沒有message物件來,即Looper處在空閒時,會實行這個idleHandler,相當於在UI執行緒中頁面的操作如onDestroy、onStop等跟AMS通訊完成後,沒有handler.post訊息message過來時,就實行了idleHandler。
public final class Looper { public static void loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; ... for (;;) { Message msg = queue.next(); // might block ... } } } public final class MessageQueue { Message next() { ... for (;;) { ... synchronized (this) { // Try to retrieve the next message.Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; ...(return msg) } for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } } } }
接著實行核心方法ensureGone:
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null. Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); removeWeaklyReachableReferences();//看下檢測的弱引用回收了沒 ... if (gone(reference)) {//好,回收了,那麼這個activity沒有洩漏 return DONE; } gcTrigger.runGc();//還是沒有回收,手動GC一下 removeWeaklyReachableReferences();//再看看物件回收沒有 if (!gone(reference)) {//還沒回收,懷疑是記憶體洩漏了,dump記憶體快照.hprof下來分析 long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); File heapDumpFile = heapDumper.dumpHeap();//生產快照file if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs)); } return DONE; } private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key); } private void removeWeaklyReachableReferences() { KeyedWeakReference ref; while ((ref = (KeyedWeakReference) queue.poll()) != null) { retainedKeys.remove(ref.key); } } }
三、hrop記憶體快照生成
解析hprof檔案中,是先把這個檔案封裝成snapshot,然後根據弱引用和前面定義的key值,確定洩漏的物件,最後找到最短洩漏路徑,作為結果反饋出來:
File heapDumpFile = heapDumper.dumpHeap();
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
生產file,進入ServiceHeapDumpListener的analyze函式:
@Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }
緊接著呼叫HeapAnalyzerService.runAnalysis:
public final class HeapAnalyzerService extends IntentService { public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); context.startService(intent); } }
startService:DisplayLeakService 中:
public class DisplayLeakService extends AbstractAnalysisResultService { @Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { String leakInfo = leakInfo(this, heapDump, result, true); ... if (shouldSaveResult) { heapDump = renameHeapdump(heapDump); resultSaved = saveResult(heapDump, result); } PendingIntent pendingIntent; ... pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey); int notificationId = (int) (SystemClock.uptimeMillis() / 1000); showNotification(this, contentTitle, contentText, pendingIntent, notificationId); afterDefaultHandling(heapDump, result, leakInfo); }