LeakCanary 工作原理浅析

参考链接
Java内存问题 及 LeakCanary 原理分析
LeakCanary 工作原理浅析

通过 application.registerActivityLifecycleCallbacks 监听activity onActivityDestroy()
用过WeakReference+ReferenceQueue 存储当前activity 的引用,手动调用gc
然后判断ReferenceQueue队列是否含有此activity的引用,若不包含,说明被强引用,存在内存泄漏,然后导出heap dump 文件,用haha算法分析,在发送到通知栏里

public final class ActivityRefWatcher {
 public static void install(Context context, 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);
        }
      };
}
public final class RefWatcher {
 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);
//创建WeakReference
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    ensureGoneAsync(watchStartNanoTime, reference);
  }
}
public final class RefWatcher {

  @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();//先将引用尝试从队列中poll出来

    if (debuggerControl.isDebuggerAttached()) {//规避调试模式
      // The debugger can create false leaks.
      return RETRY;
    }
    if (gone(reference)) {//检测是否已经回收
      return DONE;
    }
        //如果没有被回收,则手动GC
    gcTrigger.runGc();
    removeWeaklyReachableReferences();//再次尝试poll,检测是否被回收
    if (!gone(reference)) {
// 还没有被回收,则dump堆信息,调起分析进程进行分析
      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;
  }

  private boolean gone(KeyedWeakReference reference) {
    return !retainedKeys.contains(reference.key);
  }

  private void removeWeaklyReachableReferences() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    KeyedWeakReference ref;
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
      retainedKeys.remove(ref.key);
    }
  }
}

方法ensureGone中通过检测referenceQueue队列中的引用情况,来判断回收情况,通过手动GC来进一步确认回收情况。
整个过程肯定是个耗时卡UI的,整个过程会在WatchExecutor中执行的,那WatchExecutor又是在哪里执行的呢?
LeakCanary已经利用Looper机制做了一定优化,利用主线程空闲的时候执行检测任务,这里找到WatchExecutor的实现类,研究下原理:

public final class AndroidWatchExecutor implements WatchExecutor {

  private void waitForIdle(final Retryable retryable, final int failedAttempts) {
    // This needs to be called from the main thread.
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
      @Override public boolean queueIdle() {
        postToBackgroundWithDelay(retryable, failedAttempts);
        return false;
      }
    });
  }
}

知识点

  • 用ActivityLifecycleCallback 监听activity生命周期
  • 用WeekReference+ReferenceQueue 来监听activity回收情况
  • Application 中可通过processName 判断是否任务执行进程
  • MessageQueue 中加入一个IdleHandler 来得到主线程空闲的回调
  • 只能监听activity里相关类内存泄漏,其他类无法使用,还得用MAT原始方法

你可能感兴趣的:(LeakCanary 工作原理浅析)