天天看点

LeakCanary简单分析

一.基本使用
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值,确定泄漏的对象,最后找到最短泄漏路径,作为结果反馈出来,那么如果在快照中找不到这个怀疑泄漏的对象,那么就认为这个对象其实并没有泄漏,因为已经回收了,如下的代码





      

继续阅读