LeakCanary 2 源码解析(二)如何实现自动监听Activity的内存泄露?

问题2:如何实现自动监听Activity的内存泄露?

让我们回到上篇的最后,我们知道了ActivityDestroyWatcher中的install中,
注册了appcation的activity生命周期变化的监听。


image.png

监听的实现如上图。
其实就是在onActivityDestroyed的方法中,使用ObjectWatch类watch了执行到onDestroyed的activity。

ObjectWatcher是干什么的?
/**
 * [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances
 * that reference watches objects, and check if those references have been cleared as expected on
 * the [checkRetainedExecutor] executor. If not, these objects are considered retained and
 * [ObjectWatcher] will then notify the [onObjectRetainedListener] on that executor thread.
 *
 * [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant
 * to give the GC the opportunity to identify weakly reachable objects.
 *
 * [ObjectWatcher] is thread safe.
 */

在这里我把源码中的注释翻译了一下,官方的解释比较贴切。

ObjectWatch可以监视传入的对象,它会创建监视传入对象的弱引用实例,检查这些引用是否已经背checkRetainedExecutor执行者清除。如果没有,这些对象被考虑保留,然后ObjectWatcher将会在那个执行者的线程里通知onObjectRetainedListener。

checkRetainedExecutor被期望用来在后台线程运行的任务,带上一个象征,使GC有机会识别弱可及对象。

checkRetainedExecutor,是Executor的一个实现。

image.png
ObjectWatcher如何检测传入的Object的回收状态?

让我们先看看ObjectWatch的watch方法


image.png

1,removeWeaklyReachableObjects
将watcher中存储的所有未被clear的WeakReference引用入队。
2,为传入的待监视的对象创建弱引用实例。
3,交给checkRetainedExecutor去执行。

让我们继续看看moveToRetained的方法中做了什么


image.png

如果等到handler之前发送的消息开始执行的时候,也会先进行一次检查,将watcher中存储的所有未被clear的WeakReference引用入队。
如果还有弱引用还未被回收,这个时候ObjectWatcher会通知外部有对象被保留了。

onOjectRetainedListeners是在哪里注册的呢?

image.png

ObjectWatcher提供了外部设置监听的方法,往上查找调用者,发现只有InternalLeakCanary的invoke方法进行了调用。

image.png

AppWatcher.objectWatcher实际上就是InternalAppWatcher中创建的watcher,也就是传入ActivityDestroyWatcher的ObjectWatcher

  /**
   * The [ObjectWatcher] used by AppWatcher to detect retained objects.
   */
  val objectWatcher
    get() = InternalAppWatcher.objectWatcher

  /** @see [manualInstall] */
  val isInstalled
    get() = InternalAppWatcher.isInstalled

ok,现在我们知道InternalLeakCanary类中实现了OnRetainListener,我们看看它实现的方法

  override fun onObjectRetained() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.onObjectRetained()
    }
  }

在方法中最终交由HeapDumpTrigger来进行管理。

HeapDumpTrigger

让我们继续看HeapDumpTrigger的onObjectRetained方法做了什么

fun onObjectRetained() {
    scheduleRetainedObjectCheck(
        reason = "found new object retained",
        rescheduling = false
    )
  }
private fun scheduleRetainedObjectCheck(
    reason: String,
    rescheduling: Boolean,
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
      SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" }
      return
    } else {
      val verb = if (rescheduling) "Rescheduling" else "Scheduling"
      val delay = if (delayMillis > 0) " in ${delayMillis}ms" else ""
      SharkLog.d { "$verb check for retained objects${delay} because $reason" }
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      checkRetainedObjects(reason)
    }, delayMillis)
  }

关键看最后一行

backgroundHandler.postDelayed({
      checkScheduledAt = 0
      checkRetainedObjects(reason)
    }, delayMillis)

往后台线程发送了一个消息。
checkRetainedObjects方法中就是检测对象回收状态、提示、存储的主要实现了。

private fun checkRetainedObjects(reason: String) {
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
      SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
      return
    }

    var retainedReferenceCount = objectWatcher.retainedObjectCount

    if (retainedReferenceCount > 0) {
      // 如果保留的对象数量大于0,则立刻进行一次gc
      gcTrigger.runGc()
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }

      // 再进行一次检查,如果通过则直接返回
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
      //如果不允许dump堆内存,并且处于调试状态,则进行提示,并且安排下一次的检查,直接return
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_debugger_attached
          )
      )
      scheduleRetainedObjectCheck(
          reason = "debugger is attached",
          rescheduling = true,
          delayMillis = WAIT_FOR_DEBUG_MILLIS
      )
      return
    }

    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
      //如果未过dump内存的间隔,则只弹出通知栏提示,并且安排下一次检查,直接return
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
      )
      scheduleRetainedObjectCheck(
          reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
          rescheduling = true,
          delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
      )
      return
    }

      //dump内存。
    SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
    dismissRetainedCountNotification()
    dumpHeap(retainedReferenceCount, retry = true)
  }

你可能感兴趣的:(LeakCanary 2 源码解析(二)如何实现自动监听Activity的内存泄露?)