阿里一面之LeakCanary内存泄漏监测原理总结
本文通过在阿里面试遇到的问题总结而出,如有不对地方,请及时批评指正。篇幅较长,请耐心阅读。简介:
LeakCanary是一个开源的内存泄漏检查工具,使用简单,主要用来监测Activity和Fragment是否发生内存泄漏。如果发生内存泄漏,直接以引用链的形式展示出造成内存泄漏对象。使用步骤添加build.gradle依懒。 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"
2.在Application中初始化LeakCanary。class CustomApplication:MainApplication() { override fun onCreate() { super.onCreate() if (! LeakCanary.isInAnalyzerProcess(this)) { LeakCanary.install(this); } } }
完成以上两步操作之后,当打开app运行时,如果Activity或Fragment发生内存泄漏,会以通知的形式提醒用户。源码分析
先通过流程图来看一下LeakCanary工作原理,如图所示:
LeakCanary初始化
1.在application中注册。class CustomApplication:MainApplication() { override fun onCreate() { super.onCreate() if (! LeakCanary.isInAnalyzerProcess(this)) { //注册leakcanary LeakCanary.install(this); } } }
2.构建观察者RefWatcher。public static RefWatcher install(Application application) { //构建观察者RefWatcher return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); }
3.构建不同的观察者ActivityRefWatcher或FragmentRefWatcher。public RefWatcher buildAndInstall() { if (LeakCanaryInternals.installedRefWatcher != null) { throw new UnsupportedOperationException("buildAndInstall() should only be called once."); } RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { //观察activity if (watchActivities) { ActivityRefWatcher.install(context, refWatcher); } //观察fragment if (watchFragments) { FragmentRefWatcher.Helper.install(context, refWatcher); } } LeakCanaryInternals.installedRefWatcher = refWatcher; return refWatcher; }
4.监测activity生命周期。public static void install(Context context, RefWatcher refWatcher) { Application application = (Application) context.getApplicationContext(); ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); //注册生命周期 application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks); }
5.将观察对象activity添加到被观察者队列。private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed(Activity activity) { //在activity的onDestroy生命周期中添加观察对象 refWatcher.watch(activity); } };泄漏检测
1.将被观察对象包装成弱引用。public void watch(Object watchedReference, String referenceName) { if (this == DISABLED) { return; } //判空检查 checkNotNull(watchedReference, "watchedReference"); checkNotNull(referenceName, "referenceName"); final long watchStartNanoTime = System.nanoTime(); //生成唯一key String key = UUID.randomUUID().toString(); //保存key retainedKeys.add(key); //包装成弱引用对象 final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); //检查被观察对象是否被回收 ensureGoneAsync(watchStartNanoTime, reference); }
2.检测弱引用对象是否被回收。@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); //移除被回收对象的key removeWeaklyReachableReferences(); if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } //判断弱引用是否被回收 if (gone(reference)) { return DONE; } //触发系统GC进行垃圾回收 gcTrigger.runGc(); //再次移除被回收对象的key removeWeaklyReachableReferences(); //判断弱引用对象是否被回收 if (!gone(reference)) { //将没有被回收对象的内存快照保存成文件 long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. 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; }内存分析
1.启动HeapAnalyzerService进行内存分析@Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }
2.保存文件,并发送通知给用户@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) { String leakInfo = leakInfo(this, heapDump, result, true); CanaryLog.d("%s", leakInfo); boolean resultSaved = false; boolean shouldSaveResult = result.leakFound || result.failure != null; if (shouldSaveResult) { //保存内存文件 heapDump = renameHeapdump(heapDump); resultSaved = saveResult(heapDump, result); } PendingIntent pendingIntent; String contentTitle; String contentText; //解析内存文件 if (!shouldSaveResult) { contentTitle = getString(R.string.leak_canary_no_leak_title); contentText = getString(R.string.leak_canary_no_leak_text); pendingIntent = null; } else if (resultSaved) { pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey); if (result.failure == null) { if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) { String className = classSimpleName(result.className); if (result.excludedLeak) { contentTitle = getString(R.string.leak_canary_leak_excluded, className); } else { contentTitle = getString(R.string.leak_canary_class_has_leaked, className); } } else { String size = formatShortFileSize(this, result.retainedHeapSize); String className = classSimpleName(result.className); if (result.excludedLeak) { contentTitle = getString(R.string.leak_canary_leak_excluded_retaining, className, size); } else { contentTitle = getString(R.string.leak_canary_class_has_leaked_retaining, className, size); } } } else { contentTitle = getString(R.string.leak_canary_analysis_failed); } contentText = getString(R.string.leak_canary_notification_message); } else { contentTitle = getString(R.string.leak_canary_could_not_save_title); contentText = getString(R.string.leak_canary_could_not_save_text); pendingIntent = null; } // 每次发送一个新通知提醒用户。 int notificationId = (int) (SystemClock.uptimeMillis() / 1000); showNotification(this, contentTitle, contentText, pendingIntent, notificationId); afterDefaultHandling(heapDump, result, leakInfo); }
整个监测过程主要作用如下:
1.注册监听activity生命周期。
2.在activity被销毁时加入弱引用队列。
3.第一次移除不可达对象,移除ReferenceQueue中的KeyedWeakReference。
4.主动触发GC进行垃圾回收。
5.第二次移除不可达对象,移除ReferenceQueue中的KeyedWeakReference。
6.判断当前是否还有对象存活,如果有保存存活对象的内存快照heapDumpFile,然后进行内存分析。
7.启动HeapAnalyzerService对内存快照进行分析,找出GCroots引用链。
8.发送通知给用户。
以上就是阿里面试后总结的几个要点,还不会的同学赶紧学起来吧,感谢您的阅读,创造不易,如果您觉得本篇文章对您有帮助,请点击关注小编,您的支持就是小编创作的最大动力!