Leakcanary 详解

2021-12-15 15:34:14 浏览数 (1)

2.源码分析

我们在build.gradle文件中加入Leakcanary依赖库:

代码语言:txt复制
 debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'

LeakCanary的使用从LeakCanary.install(this)开始,

下面我们从入口开始分析:

代码语言:txt复制
//LeakCanary.java
代码语言:txt复制
 /**
代码语言:txt复制
   * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
   * references (on ICS ).
   */
  public static @NonNull RefWatcher install(@NonNull Application application) {
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }
代码语言:txt复制
  public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
代码语言:txt复制
    return new AndroidRefWatcherBuilder(context);
代码语言:txt复制
  }

builder模式构建了一个RefWatcher对象,listenerServiceClass()方法绑定了一个后台服务DisplayLeakService

这个服务主要用来分析内存泄漏结果并发送通知。你可以继承并重写这个类来进行一些自定义操作,比如上传分析结果等。

我们最后看buildAndInstall()方法:

代码语言:txt复制
#AndroidRefWatcherBuilder.java
代码语言:txt复制
 /**
代码语言:txt复制
   * Creates a {@link RefWatcher} instance and makes it available through {@link
   * LeakCanary#installedRefWatcher()}.
   *
   * Also starts watching activity references if {@link #watchActivities(boolean)} was set to true.
   *
   * @throws UnsupportedOperationException if called more than once per Android process.
   */
  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;
  }
代码语言:txt复制
  /** Creates a {@link RefWatcher}. */
代码语言:txt复制
  public final RefWatcher build() {
代码语言:txt复制
    if (isDisabled()) {
代码语言:txt复制
      return RefWatcher.DISABLED;
代码语言:txt复制
    }
代码语言:txt复制
    if (heapDumpBuilder.excludedRefs == null) {
代码语言:txt复制
      heapDumpBuilder.excludedRefs(defaultExcludedRefs());
代码语言:txt复制
    }
代码语言:txt复制
    HeapDump.Listener heapDumpListener = this.heapDumpListener;
代码语言:txt复制
    if (heapDumpListener == null) {
代码语言:txt复制
      heapDumpListener = defaultHeapDumpListener();
代码语言:txt复制
    }
代码语言:txt复制
    DebuggerControl debuggerControl = this.debuggerControl;
代码语言:txt复制
    if (debuggerControl == null) {
代码语言:txt复制
      debuggerControl = defaultDebuggerControl();
代码语言:txt复制
    }
代码语言:txt复制
    HeapDumper heapDumper = this.heapDumper;
代码语言:txt复制
    if (heapDumper == null) {
代码语言:txt复制
      heapDumper = defaultHeapDumper();
代码语言:txt复制
    }
代码语言:txt复制
    WatchExecutor watchExecutor = this.watchExecutor;
代码语言:txt复制
    if (watchExecutor == null) {
代码语言:txt复制
      watchExecutor = defaultWatchExecutor();
代码语言:txt复制
    }
代码语言:txt复制
    GcTrigger gcTrigger = this.gcTrigger;
代码语言:txt复制
    if (gcTrigger == null) {
代码语言:txt复制
      gcTrigger = defaultGcTrigger();
代码语言:txt复制
    }
代码语言:txt复制
    if (heapDumpBuilder.reachabilityInspectorClasses == null) {
代码语言:txt复制
      heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
代码语言:txt复制
    }
代码语言:txt复制
    return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
代码语言:txt复制
        heapDumpBuilder);
代码语言:txt复制
  }

build()方法,这个方法主要是配置一些东西,先大概了解一下,后面用到再说,下面是几个配置项目。

  • watchExecutor : 线程控制器,在 onDestroy()之后并且主线程空闲时执行内存泄漏检测
  • debuggerControl: 判断是否处于调试模式,调试模式中不会进行内存泄漏检测
  • gcTrigger: 用于GC
  • watchExecutor首次检测到可能的内存泄漏,会主动进行GC,GC之后会再检测一次,仍然泄漏的判定为内存泄漏,进行后续操作
  • heapDumper: dump内存泄漏处的heap信息,写入hprof文件
  • heapDumpListener: 解析完hprof文件并通知DisplayLeakService弹出提醒
  • excludedRefs: 排除可以忽略的泄漏路径
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class,

true);

这行代码主要是为了开启LeakCanary的应用,显示其图标.

具体实现如下:

代码语言:txt复制
#LeakCanaryInternals.java
代码语言:txt复制
  public static void setEnabledAsync(Context context, final Class<?> componentClass,
代码语言:txt复制
      final boolean enabled) {
代码语言:txt复制
    final Context appContext = context.getApplicationContext();
代码语言:txt复制
    AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
代码语言:txt复制
      @Override public void run() {
代码语言:txt复制
        setEnabledBlocking(appContext, componentClass, enabled);
代码语言:txt复制
      }
代码语言:txt复制
    });
代码语言:txt复制
  }
代码语言:txt复制
 public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
代码语言:txt复制
      boolean enabled) {
代码语言:txt复制
    ComponentName component = new ComponentName(appContext, componentClass);
代码语言:txt复制
    PackageManager packageManager = appContext.getPackageManager();
代码语言:txt复制
    int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
代码语言:txt复制
    // Blocks on IPC.
代码语言:txt复制
    packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
代码语言:txt复制
  }

接下来是重点:

ActivityRefWatcher.install(context, refWatcher);

源码如下:

代码语言:txt复制
#ActivityRefWatcher.java
代码语言:txt复制
public final class ActivityRefWatcher {
代码语言:txt复制
  public static void installOnIcsPlus(@NonNull Application application,
代码语言:txt复制
      @NonNull RefWatcher refWatcher) {
代码语言:txt复制
    install(application, refWatcher);
代码语言:txt复制
  }
代码语言:txt复制
  public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
代码语言:txt复制
    Application application = (Application) context.getApplicationContext();
代码语言:txt复制
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
代码语言:txt复制
    application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
代码语言:txt复制
  }
代码语言:txt复制
//注释1:Activity生命周期回调接口
代码语言:txt复制
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
代码语言:txt复制
      new ActivityLifecycleCallbacksAdapter() {
代码语言:txt复制
        @Override public void onActivityDestroyed(Activity activity) {
代码语言:txt复制
        //注释2:监听到Activity销毁的时候执行
代码语言:txt复制
         refWatcher.watch(activity);
代码语言:txt复制
        }
代码语言:txt复制
      };
代码语言:txt复制
  private final Application application;
代码语言:txt复制
  private final RefWatcher refWatcher;
代码语言:txt复制
  private ActivityRefWatcher(Application application, RefWatcher refWatcher) {
代码语言:txt复制
    this.application = application;
代码语言:txt复制
    this.refWatcher = refWatcher;
代码语言:txt复制
  }
代码语言:txt复制
  public void watchActivities() {
代码语言:txt复制
    // Make sure you don't get installed twice.
代码语言:txt复制
    stopWatchingActivities();
代码语言:txt复制
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
代码语言:txt复制
  }
代码语言:txt复制
  public void stopWatchingActivities() {
代码语言:txt复制
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
代码语言:txt复制
  }
代码语言:txt复制
}

我们主要看注释2:整个LeakCanary最核心的思路就在这儿。

代码语言:txt复制
/**
代码语言:txt复制
 * Watches references that should become weakly reachable. When the {@link RefWatcher} detects that
 * a reference might not be weakly reachable when it should, it triggers the {@link HeapDumper}.
 *
 * <p>This class is thread-safe: you can call {@link #watch(Object)} from any thread.
 */
public final class RefWatcher {
代码语言:txt复制
  public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
代码语言:txt复制
  private final WatchExecutor watchExecutor;
代码语言:txt复制
  private final DebuggerControl debuggerControl;
代码语言:txt复制
  private final GcTrigger gcTrigger;
代码语言:txt复制
  private final HeapDumper heapDumper;
代码语言:txt复制
  private final HeapDump.Listener heapdumpListener;
代码语言:txt复制
  private final HeapDump.Builder heapDumpBuilder;
代码语言:txt复制
  private final Set<String> retainedKeys;
代码语言:txt复制
  private final ReferenceQueue<Object> queue;
代码语言:txt复制
  RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,
代码语言:txt复制
      HeapDumper heapDumper, HeapDump.Listener heapdumpListener, HeapDump.Builder heapDumpBuilder) {
代码语言:txt复制
    this.watchExecutor = checkNotNull(watchExecutor, "watchExecutor");
代码语言:txt复制
    this.debuggerControl = checkNotNull(debuggerControl, "debuggerControl");
代码语言:txt复制
    this.gcTrigger = checkNotNull(gcTrigger, "gcTrigger");
代码语言:txt复制
    this.heapDumper = checkNotNull(heapDumper, "heapDumper");
代码语言:txt复制
    this.heapdumpListener = checkNotNull(heapdumpListener, "heapdumpListener");
代码语言:txt复制
    this.heapDumpBuilder = heapDumpBuilder;
代码语言:txt复制
    retainedKeys = new CopyOnWriteArraySet<>();
代码语言:txt复制
    queue = new ReferenceQueue<>();
代码语言:txt复制
  }
代码语言:txt复制
  /**
代码语言:txt复制
   * Identical to {@link #watch(Object, String)} with an empty string reference name.
   *
   * @see #watch(Object, String)
   */
  public void watch(Object watchedReference) {
    watch(watchedReference, "");
  }
代码语言:txt复制
  /**
代码语言:txt复制
   * Watches the provided references and checks if it can be GCed. This method is non blocking,
   * the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
   * with.
   *
   * @param referenceName An logical identifier for the watched object.
   */
  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);
代码语言:txt复制
    ensureGoneAsync(watchStartNanoTime, reference);
代码语言:txt复制
  }
代码语言:txt复制
  /**
代码语言:txt复制
   * LeakCanary will stop watching any references that were passed to {@link #watch(Object, String)}
   * so far.
   */
  public void clearWatchedReferences() {
    retainedKeys.clear();
  }
代码语言:txt复制
  boolean isEmpty() {
代码语言:txt复制
    removeWeaklyReachableReferences();
代码语言:txt复制
    return retainedKeys.isEmpty();
代码语言:txt复制
  }
代码语言:txt复制
  HeapDump.Builder getHeapDumpBuilder() {
代码语言:txt复制
    return heapDumpBuilder;
代码语言:txt复制
  }
代码语言:txt复制
  Set<String> getRetainedKeys() {
代码语言:txt复制
    return new HashSet<>(retainedKeys);
代码语言:txt复制
  }
代码语言:txt复制
  private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
代码语言:txt复制
    watchExecutor.execute(new Retryable() {
代码语言:txt复制
      @Override public Retryable.Result run() {
代码语言:txt复制
        return ensureGone(reference, watchStartNanoTime);
代码语言:txt复制
      }
代码语言:txt复制
    });
代码语言:txt复制
  }
代码语言:txt复制
  @SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
代码语言:txt复制
  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
代码语言:txt复制
    long gcStartNanoTime = System.nanoTime();
代码语言:txt复制
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
代码语言:txt复制
    removeWeaklyReachableReferences();
代码语言:txt复制
    if (debuggerControl.isDebuggerAttached()) {
代码语言:txt复制
      // The debugger can create false leaks.
代码语言:txt复制
      return RETRY;
代码语言:txt复制
    }
代码语言:txt复制
    if (gone(reference)) {
代码语言:txt复制
      return DONE;
代码语言:txt复制
    }
代码语言:txt复制
    gcTrigger.runGc();
代码语言:txt复制
    removeWeaklyReachableReferences();
代码语言:txt复制
    if (!gone(reference)) {
代码语言:txt复制
      long startDumpHeap = System.nanoTime();
代码语言:txt复制
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
代码语言:txt复制
      File heapDumpFile = heapDumper.dumpHeap();
代码语言:txt复制
      if (heapDumpFile == RETRY_LATER) {
代码语言:txt复制
        // Could not dump the heap.
代码语言:txt复制
        return RETRY;
代码语言:txt复制
      }
代码语言:txt复制
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
代码语言:txt复制
      HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
代码语言:txt复制
          .referenceName(reference.name)
代码语言:txt复制
          .watchDurationMs(watchDurationMs)
代码语言:txt复制
          .gcDurationMs(gcDurationMs)
代码语言:txt复制
          .heapDumpDurationMs(heapDumpDurationMs)
代码语言:txt复制
          .build();
代码语言:txt复制
      heapdumpListener.analyze(heapDump);
代码语言:txt复制
    }
代码语言:txt复制
    return DONE;
代码语言:txt复制
  }
代码语言:txt复制
  private boolean gone(KeyedWeakReference reference) {
代码语言:txt复制
    return !retainedKeys.contains(reference.key);
代码语言:txt复制
  }
代码语言:txt复制
  private void removeWeaklyReachableReferences() {
代码语言:txt复制
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
代码语言:txt复制
    // reachable. This is before finalization or garbage collection has actually happened.
代码语言:txt复制
    KeyedWeakReference ref;
代码语言:txt复制
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
代码语言:txt复制
      retainedKeys.remove(ref.key);
代码语言:txt复制
    }
代码语言:txt复制
  }
代码语言:txt复制
}

这个函数做的主要工作就是为需要进行泄漏检查的Activity创建一个带有唯一key标志的弱引用,并将这个弱引用key保存至retainedKeys中,然后将后面的工作交给watchExecutor来执行。这个watchExecutor在LeakCanary中是AndroidWatchExecutor的实例,调用它的execute方法实际上就是向主线程的消息队列中插入了一个IdleHandler消息,这个消息只有在对应的消息队列为空的时候才会去执行,因此RefWatcher的watch方法就保证了在主线程空闲的时候才会去执行ensureGone方法,防止因为内存泄漏检查任务而严重影响应用的正常执行。

这里引出了第一个知识点,弱引用和引用队列ReferenceQueue联合使用时,如果弱引用持有的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。即

KeyedWeakReference持有的Activity对象如果被垃圾回收,该对象就会加入到引用队列queue,我们看看RefreceQueue的javadoc:

代码语言:txt复制
/**
代码语言:txt复制
 * Reference queues, to which registered reference objects are appended by the
 * garbage collector after the appropriate reachability changes are detected.
 *
 * @author   Mark Reinhold
 * @since    1.2
 */
// BEGIN Android-changed: Reimplemented to accomodate a different GC and compiler.
代码语言:txt复制
public class ReferenceQueue<T>

重点是最后一句:ensureGoneAysnc,看字面意思,异步确保消失。这里我们先不看代码,如果自己设计一套检测方案的话,怎么想?其实很简单,就是在Activity

onDestory以后,我们等一会,检测一下这个Activity有没有被回收,那么问题来了,什么时候检测?怎么检测?这也是本框架的核心和难点。

LeakCanary是这么做的:onDestroy以后,一旦主线程空闲下来,延时5秒执行一个任务:先判断Activity有没有被回收?如果已经回收了,说明没有内存泄漏,如果还没回收,我们进一步确认,手动触发一下gc,然后再判断有没有回收,如果这次还没回收,说明Activity确实泄漏了,接下来把泄漏的信息展示给开发者就好了。

我们看代码实现:

代码语言:txt复制
 private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
代码语言:txt复制
    watchExecutor.execute(new Retryable() {
代码语言:txt复制
      @Override public Retryable.Result run() {
代码语言:txt复制
        return ensureGone(reference, watchStartNanoTime);
代码语言:txt复制
      }
代码语言:txt复制
    });
代码语言:txt复制
  }

这里的watchExecutor是AndroidWatchExecutor,看代码:

代码语言:txt复制
/**
代码语言:txt复制
 * {@link WatchExecutor} suitable for watching Android reference leaks. This executor waits for the
 * main thread to be idle then posts to a serial background thread with the delay specified by
 * {@link AndroidRefWatcherBuilder#watchDelay(long, TimeUnit)}.
 */
public final class AndroidWatchExecutor implements WatchExecutor {
代码语言:txt复制
  static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
代码语言:txt复制
  private final Handler mainHandler;
代码语言:txt复制
  private final Handler backgroundHandler;
代码语言:txt复制
  private final long initialDelayMillis;
代码语言:txt复制
  private final long maxBackoffFactor;
代码语言:txt复制
  public AndroidWatchExecutor(long initialDelayMillis) {
代码语言:txt复制
    mainHandler = new Handler(Looper.getMainLooper());
代码语言:txt复制
    HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
代码语言:txt复制
    handlerThread.start();
代码语言:txt复制
    backgroundHandler = new Handler(handlerThread.getLooper());
代码语言:txt复制
    this.initialDelayMillis = initialDelayMillis;
代码语言:txt复制
    maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
代码语言:txt复制
  }
代码语言:txt复制
  @Override public void execute(@NonNull Retryable retryable) {
代码语言:txt复制
    if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
代码语言:txt复制
      waitForIdle(retryable, 0);
代码语言:txt复制
    } else {
代码语言:txt复制
      postWaitForIdle(retryable, 0);
代码语言:txt复制
    }
代码语言:txt复制
  }
代码语言:txt复制
  private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
代码语言:txt复制
    mainHandler.post(new Runnable() {
代码语言:txt复制
      @Override public void run() {
代码语言:txt复制
        waitForIdle(retryable, failedAttempts);
代码语言:txt复制
      }
代码语言:txt复制
    });
代码语言:txt复制
  }
代码语言:txt复制
  private void waitForIdle(final Retryable retryable, final int failedAttempts) {
代码语言:txt复制
    // This needs to be called from the main thread.
代码语言:txt复制
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
代码语言:txt复制
      @Override public boolean queueIdle() {
代码语言:txt复制
        postToBackgroundWithDelay(retryable, failedAttempts);
代码语言:txt复制
        return false;
代码语言:txt复制
      }
代码语言:txt复制
    });
代码语言:txt复制
  }
代码语言:txt复制
  private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
代码语言:txt复制
    long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
代码语言:txt复制
    long delayMillis = initialDelayMillis * exponentialBackoffFactor;
代码语言:txt复制
    backgroundHandler.postDelayed(new Runnable() {
代码语言:txt复制
      @Override public void run() {
代码语言:txt复制
        Retryable.Result result = retryable.run();
代码语言:txt复制
        if (result == RETRY) {
代码语言:txt复制
          postWaitForIdle(retryable, failedAttempts   1);
代码语言:txt复制
        }
代码语言:txt复制
      }
代码语言:txt复制
    }, delayMillis);
代码语言:txt复制
  }
代码语言:txt复制
}

这里有第二个知识点,IdleHandler,这个东西是干嘛的,其实看名字就知道了,就是当主线程空闲的时候,如果设置了这个东西,就会执行它的queueIdle()方法,所以这个方法就是在onDestory以后,一旦主线程空闲了,就会执行,然后我们看它执行了啥:

代码语言:txt复制
  private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
代码语言:txt复制
    long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
代码语言:txt复制
    long delayMillis = initialDelayMillis * exponentialBackoffFactor;
代码语言:txt复制
    backgroundHandler.postDelayed(new Runnable() {
代码语言:txt复制
      @Override public void run() {
代码语言:txt复制
        Retryable.Result result = retryable.run();
代码语言:txt复制
        if (result == RETRY) {
代码语言:txt复制
          postWaitForIdle(retryable, failedAttempts   1);
代码语言:txt复制
        }
代码语言:txt复制
      }
代码语言:txt复制
    }, delayMillis);
代码语言:txt复制
  }

延时5秒执行retryable的run(),注意,因为这里是backgroundHandler post出来的,所以是下面的run 是在子线程执行的。

ensureGone(reference,

watchStartNanoTime),在看它干了啥之前,我们先理下思路,前面onDestory以后,AndroidWatchExecutor这个东西执行excute方法,这个方法让主线程在空闲的时候发送了一个延时任务,该任务在5秒延时后在一个子线程执行。理清了思路,我们看看这个任务是怎么执行的。

代码语言:txt复制
 Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
代码语言:txt复制
    long gcStartNanoTime = System.nanoTime();
代码语言:txt复制
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
代码语言:txt复制
    removeWeaklyReachableReferences();
代码语言:txt复制
    if (debuggerControl.isDebuggerAttached()) {
代码语言:txt复制
      // The debugger can create false leaks.
代码语言:txt复制
      return RETRY;
代码语言:txt复制
    }
代码语言:txt复制
    if (gone(reference)) {
代码语言:txt复制
      return DONE;
代码语言:txt复制
    }
代码语言:txt复制
    gcTrigger.runGc(); // 手动执行一次gc
代码语言:txt复制
    removeWeaklyReachableReferences();
代码语言:txt复制
    if (!gone(reference)) {
代码语言:txt复制
      long startDumpHeap = System.nanoTime();
代码语言:txt复制
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
代码语言:txt复制
      File heapDumpFile = heapDumper.dumpHeap();
代码语言:txt复制
      if (heapDumpFile == RETRY_LATER) {
代码语言:txt复制
        // Could not dump the heap.
代码语言:txt复制
        return RETRY;
代码语言:txt复制
      }
代码语言:txt复制
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
代码语言:txt复制
      HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
代码语言:txt复制
          .referenceName(reference.name)
代码语言:txt复制
          .watchDurationMs(watchDurationMs)
代码语言:txt复制
          .gcDurationMs(gcDurationMs)
代码语言:txt复制
          .heapDumpDurationMs(heapDumpDurationMs)
代码语言:txt复制
          .build();
代码语言:txt复制
      heapdumpListener.analyze(heapDump);
代码语言:txt复制
    }
代码语言:txt复制
    return DONE;
代码语言:txt复制
  }

因为这个方法是在主线程中执行的,因此一般执行到这个方法中的时候之前被destroy的那个Activity的资源应该被JVM回收了,因此这个方法首先调用removeWeaklyReachableReferences方法来将引用

队列中存在的弱引用从retainedKeys中删除掉,这样,retainedKeys中保留的就是还没有被回收对象的弱引用key。之后再用gone方法来

判断我们需要检查的Activity的弱引用是否在retainedKeys中,如果没有,说明这个Activity对象已经被回收,检查结束。否则,

LeakCanary主动触发一次gc,再进行以上两个步骤,如果发现这个Activity还没有被回收,就认为这个Activity很有可能泄漏了,并dump出当前的内存文件供之后进行分析。

前面我们说过,5秒延迟后先看看有没有回收,如果回收了,直接返回,没有发生内存泄漏,如果没有回收,触发GC,gc完成后,在此判断有没有回收,如果还没有回收,说明泄漏了,收集泄漏信息,展示给开发者。其中,removeWeaklyRechableReferences()和gone(reference)这两个方法配合,用来判断对象是否被回收了,看代码:

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

通过知识点1知道:被回收的对象都会放到设置的引用队列queue中,我们从queue中拿出所有的ref,根据他们的key匹配retainedKeys集合中的元素并删除。然后在gone()函数里面判断key是否被移除.

代码语言:txt复制
  private boolean gone(KeyedWeakReference reference) {
代码语言:txt复制
    return !retainedKeys.contains(reference.key);
代码语言:txt复制
  }

这个方法挺巧妙的,retainedKeys集合了所有destoryed了的但没有被回收的Activity的key,这个集合可以用来判断一个Activity有没有被回收,但是判断之前需要用removeWeaklyReachableReferences()这个方法更新一下。

0 人点赞