概述
LeakCanary 是一个开源的内存泄漏检测库,极大简化了内存泄漏的检测流程。了解其工作原理,有助于我们更好的理解Android的内存管理机制。
使用示例 在build.gradle
中添加配置:
1 2 3 4 5 6 dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' }
在Application
类中添加代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ExampleApplication extends Application { @Override public void onCreate () { super .onCreate(); if (LeakCanary.isInAnalyzerProcess(this )) { return ; } LeakCanary.install(this ); } }
使用RefWatcher观察那些本该被GC回收掉的对象:
1 2 3 4 RefWatcher refWatcher = LeakCanary.installedRefWatcher(); refWatcher.watch(schrodingerCat);
工作机制
RefWatcher.watch()
创建一个KeyedWeakReference
到要被监控的对象。
然后在后台线程检查引用是否被清除,如果没有,调用GC。
如果引用还是未被清除,把heap内存dump到APP对应的文件系统中的一个.hprof
文件中。
在另一个进程中的HeapAnalyzerService
有一个HeapAnalyzer
使用HAHA 解析这个文件。
在Heap Dump中,HeapAnalyzer
根据唯一的reference key找到了KeyedWeakReference
,并定位了泄漏的引用。
HeapAnalyzer
计算到GC Roots
的最短强引用路径,并确定是否泄漏,如果是的话,建立导致泄漏的引用链。
引用链传递到APP进程中的DisplayLeakService
,并以通知的形式展示出来。
源码分析 创建RefWatcher 1 2 3 4 5 6 7 8 9 10 public final class LeakCanary { public static @NonNull RefWatcher install (@NonNull Application application) { return refWatcher(application) .listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); } }
AndroidRefWatcherBuilder#buildAndInstall
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public final class AndroidRefWatcherBuilder extends RefWatcherBuilder <AndroidRefWatcherBuilder > { public @NonNull RefWatcher buildAndInstall () { if (LeakCanaryInternals.installedRefWatcher != null ) { throw new UnsupportedOperationException("buildAndInstall() should only be called once." ); } RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { if (enableDisplayLeakActivity) { LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true ); } if (watchActivities) { ActivityRefWatcher.install(context, refWatcher); } if (watchFragments) { FragmentRefWatcher.Helper.install(context, refWatcher); } } LeakCanaryInternals.installedRefWatcher = refWatcher; return refWatcher; } }
监听Activity的引用 ActivityRefWatcher
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public final class ActivityRefWatcher { public static void install (@NonNull Context context, @NonNull RefWatcher refWatcher) { Application application = (Application) context.getApplicationContext(); ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks); } private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed (Activity activity) { refWatcher.watch(activity); } }; }
检查引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public final class RefWatcher { public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build(); private final WatchExecutor watchExecutor; private final DebuggerControl debuggerControl; private final GcTrigger gcTrigger; private final HeapDumper heapDumper; private final HeapDump.Listener heapdumpListener; private final HeapDump.Builder heapDumpBuilder; private final Set<String> retainedKeys; private final ReferenceQueue<Object> queue; public void watch (Object watchedReference) { watch(watchedReference, "" ); } public void watch (Object watchedReference, String referenceName) { if (this == DISABLED) { return ; } checkNotNull(watchedReference, "watchedReference" ); checkNotNull(referenceName, "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); } }); } @SuppressWarnings ("ReferenceEquality" ) Retryable.Result ensureGone (final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); removeWeaklyReachableReferences(); if (debuggerControl.isDebuggerAttached()) { return RETRY; } if (gone(reference)) { return DONE; } gcTrigger.runGc(); removeWeaklyReachableReferences(); if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == RETRY_LATER) { return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key) .referenceName(reference.name) .watchDurationMs(watchDurationMs) .gcDurationMs(gcDurationMs) .heapDumpDurationMs(heapDumpDurationMs) .build(); heapdumpListener.analyze(heapDump); } 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); } } }
Dump Heap
AndroidHeapDumper是HeapDumper的实现类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public final class AndroidHeapDumper implements HeapDumper { @Override @Nullable public File dumpHeap () { File heapDumpFile = leakDirectoryProvider.newHeapDumpFile(); if (heapDumpFile == RETRY_LATER) { return RETRY_LATER; } try { Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); cancelToast(toast); notificationManager.cancel(notificationId); return heapDumpFile; } catch (Exception e) { CanaryLog.d(e, "Could not dump heap" ); return RETRY_LATER; } } }
解析hprof 1 2 3 4 5 6 7 8 9 10 public final class ServiceHeapDumpListener implements HeapDump .Listener { @Override public void analyze (@NonNull HeapDump heapDump) { checkNotNull(heapDump, "heapDump" ); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener { public static void runAnalysis (Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { setEnabledBlocking(context, HeapAnalyzerService.class, true ); setEnabledBlocking(context, listenerServiceClass, true ); Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); ContextCompat.startForegroundService(context, intent); } @Override protected void onHandleIntentInForeground (@Nullable Intent intent) { if (intent == null ) { CanaryLog.d("HeapAnalyzerService received a null intent, ignoring." ); return ; } String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA); HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA); HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs, this , heapDump.reachabilityInspectorClasses); AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey, heapDump.computeRetainedHeapSize); AbstractAnalysisResultService.sendResultToListener(this , listenerClassName, heapDump, result); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public final class HeapAnalyzer { public @NonNull AnalysisResult checkForLeak (@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) { long analysisStartNanoTime = System.nanoTime(); if (!heapDumpFile.exists()) { Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile); return failure(exception, since(analysisStartNanoTime)); } try { listener.onProgressUpdate(READING_HEAP_DUMP_FILE); HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); HprofParser parser = new HprofParser(buffer); listener.onProgressUpdate(PARSING_HEAP_DUMP); Snapshot snapshot = parser.parse(); listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS); deduplicateGcRoots(snapshot); listener.onProgressUpdate(FINDING_LEAKING_REF); Instance leakingRef = findLeakingReference(referenceKey, snapshot); if (leakingRef == null ) { String className = leakingRef.getClassObj().getClassName(); return noLeak(className, since(analysisStartNanoTime)); } return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize); } catch (Throwable e) { return failure(e, since(analysisStartNanoTime)); } } }
定位泄露的引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public final class HeapAnalyzer { private Instance findLeakingReference (String key, Snapshot snapshot) { ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName()); if (refClass == null ) { throw new IllegalStateException( "Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump." ); } List<String> keysFound = new ArrayList<>(); for (Instance instance : refClass.getInstancesList()) { List<ClassInstance.FieldValue> values = classInstanceValues(instance); Object keyFieldValue = fieldValue(values, "key" ); if (keyFieldValue == null ) { keysFound.add(null ); continue ; } String keyCandidate = asString(keyFieldValue); if (keyCandidate.equals(key)) { return fieldValue(values, "referent" ); } keysFound.add(keyCandidate); } throw new IllegalStateException("Could not find weak reference with key " + key + " in " + keysFound); } }
建立引用链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public final class HeapAnalyzer { private AnalysisResult findLeakTrace (long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) { listener.onProgressUpdate(FINDING_SHORTEST_PATH); ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs); ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef); String className = leakingRef.getClassObj().getClassName(); if (result.leakingNode == null ) { return noLeak(className, since(analysisStartNanoTime)); } listener.onProgressUpdate(BUILDING_LEAK_TRACE); LeakTrace leakTrace = buildLeakTrace(result.leakingNode); long retainedSize; if (computeRetainedSize) { listener.onProgressUpdate(COMPUTING_DOMINATORS); snapshot.computeDominators(); Instance leakingInstance = result.leakingNode.instance; retainedSize = leakingInstance.getTotalRetainedSize(); if (SDK_INT <= N_MR1) { listener.onProgressUpdate(COMPUTING_BITMAP_SIZE); retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance); } } else { retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED; } return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize, since(analysisStartNanoTime)); } }
展示分析结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class DisplayLeakService extends AbstractAnalysisResultService { @Override protected final void onHeapAnalyzed (@NonNull AnalyzedHeap analyzedHeap) { HeapDump heapDump = analyzedHeap.heapDump; AnalysisResult result = analyzedHeap.result; String leakInfo = leakInfo(this , heapDump, result, true ); CanaryLog.d("%s" , leakInfo); heapDump = renameHeapdump(heapDump); boolean resultSaved = saveResult(heapDump, result); String contentTitle; if (resultSaved) { PendingIntent pendingIntent = DisplayLeakActivity.createPendingIntent(this , heapDump.referenceKey); if (result.failure != null ) { contentTitle = getString(R.string.leak_canary_analysis_failed); } else { String className = classSimpleName(result.className); } String contentText = getString(R.string.leak_canary_notification_message); showNotification(pendingIntent, contentTitle, contentText); } else { onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text)); } afterDefaultHandling(heapDump, result, leakInfo); } @Override protected final void onAnalysisResultFailure (String failureMessage) { super .onAnalysisResultFailure(failureMessage); String failureTitle = getString(R.string.leak_canary_result_failure_title); showNotification(null , failureTitle, failureMessage); } }
总结
参考链接
带你读懂 Reference 和 ReferenceQueue
一步步拆解 LeakCanary
深入理解Leakcanary源码
LeakCanary中文使用说明
LeakCanary:让内存泄漏无所遁形
深入理解 Android 之 LeakCanary 源码解析
Customizing LeakCanary