前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Leakcanary 详解

Leakcanary 详解

原创
作者头像
大发明家
发布2021-12-15 15:34:14
3450
发布2021-12-15 15:34:14
举报
文章被收录于专栏:技术博客文章

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()这个方法更新一下。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 2.源码分析
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档