一.基本使用 if (LeakCanary.isInAnalyzerProcess(this)) { return; } LeakCanary.install(this); 二.源码分析 1.public static RefWatcher install(Application application) { return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); } listenerServiceClass方法是和结果分析相关的服务绑定,绑定到DisplayLeakService.class这个类上面,这个类负责通知泄漏消息给你 excludedRefs方法是排除一些开发可以忽略的泄漏路径(一般是系统级别BUG),这些枚举在AndroidExcludedRefs这个类当中定义 buildAndInstall这才是重点方法: public RefWatcher buildAndInstall() { RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { LeakCanary.enableDisplayLeakActivity(context); ActivityRefWatcher.install((Application) context, refWatcher); } return refWatcher; } 上面没啥了,无非就是实例化RefWatcher对象,这个对象是用来判断泄漏对象的 public static void install(Application application, RefWatcher refWatcher) { new ActivityRefWatcher(application, refWatcher).watchActivities(); } public void watchActivities() { // Make sure you don't get installed twice. stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); } 核心方法找到了,就是registerActivityLifecycleCallbacks这个方法,这是application提供的一个方法,用来统一管理所有activity的生命周期,LeakCanay是在onDestory()方法实现监控的: private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { ActivityRefWatcher.this.onActivityDestroyed(activity); } }; 之后来到watch(Object watchedReference, String referenceName)方法: public void watch(Object watchedReference, String referenceName) { if (this != DISABLED) { Preconditions.checkNotNull(watchedReference, "watchedReference"); Preconditions.checkNotNull(referenceName, "referenceName"); long watchStartNanoTime = System.nanoTime(); String key = UUID.randomUUID().toString(); this.retainedKeys.add(key); KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, this.queue); this.ensureGoneAsync(watchStartNanoTime, reference); } } 上面做的事情就是把activity对象封装成带key值和带引用队列(ReferenceQueue)的KeyedWeakReference对象,key值是用来最终定位泄漏对象用的,第三部分会用到,引用队列是用来监控弱引用回收的,这个类是继承WeakReference类,所以封装完你会看到这个类的一些特点: 1.带key,通过UUID.randomUUID().toString(),是唯一key序列 2.包装成WeakReference并添加到ReferenceQueue 之后呢,封装完成就要开始分析了,核心方法是ensureGone,同时也是LeakCanay的核心所在,所以我注释了几个关键步骤: Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); removeWeaklyReachableReferences(); //看下检测的弱引用回收了没 if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } //好,回收了,那么这个activity没有泄漏 if (gone(reference)) { return DONE; } //还是没有回收,手动GC一下 gcTrigger.runGc(); //在看看对象回收没有 removeWeaklyReachableReferences(); //竟然还没回收,那么怀疑是内存泄漏了,所以下一步dump内存快照.hprof下来进一步精确分析 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); heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs)); } return DONE; } 如果这个对象作为弱引用,被回收了,那么添加到引用队列(ReferenceQueue)当中去,所以这个函数.poll是出栈的意思,如果成功出栈了,那么说明你加入了引用队列,然后可以认为是已经被回收了的,然后retainedKeys这个是一个Set容器,在之前会加入生成的唯一key作为标识,这里如果这个对象回收了,那么就移除这个key值。 然后是gone函数,就是看retainedKeys容器有没有key,如果回收了,就不存在key了,那么就没有泄漏,否则就怀疑有泄漏。然后后面的手动GC和检查都是一个类似二次确认的道理,还是没有回收,那么才会进入精确阶段,.hropf分析大法,这是第三部分内容。 三 .hropf内存快照分析 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)); } try { HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); HprofParser parser = new HprofParser(buffer); //1.把.hprof转为Snapshot,这个Snapshot对象就包含了对象引用的所有路径 Snapshot snapshot = parser.parse(); //2.精简gcroots deduplicateGcRoots(snapshot); //3.找出泄漏的对象 Instance leakingRef = findLeakingReference(referenceKey, snapshot); // False alarm, weak reference was cleared in between key check and heap dump. if (leakingRef == null) { return noLeak(since(analysisStartNanoTime)); } //4.找出泄漏对象的最短路径 return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef); } catch (Throwable e) { return failure(e, since(analysisStartNanoTime)); } } 总结:在第三个分析步骤,解析hprof文件中,是先把这个文件封装成snapshot,然后根据弱引用和前面定义的key值,确定泄漏的对象,最后找到最短泄漏路径,作为结果反馈出来,那么如果在快照中找不到这个怀疑泄漏的对象,那么就认为这个对象其实并没有泄漏,因为已经回收了,如下的代码
LeakCanary简单分析
最新推荐文章于 2024-05-09 13:33:50 发布