LeakCanary 源码和原理浅析

71 阅读3分钟

LeakCanary 核心原理

LeakCanary 的内存泄漏检测基于 弱引用 + 引用队列 的机制。主要分为以下几个阶段:

  1. 对象监控阶段
  2. 弱引用机制
  3. 引用队列检测
  4. 泄漏判断机制
  5. 堆转储与分析
  6. 引用链分析

1. 对象监控阶段

核心组件:

  • ObjectWatcher - 对象观察器
  • KeyedWeakReference - 带键的弱引用
  • ReferenceQueue - 引用队列

ObjectWatcher

LeakCanary 中共有 4 个主要的 Watcher,它们分别是:

  1. ActivityWatcher
  2. FragmentAndViewModelWatcher
  3. ServiceWatcher
  4. RootViewWatcher

每个 Watcher 都实现了 InstallableWatcher 接口,具有 install() 和 uninstall() 方法,可以灵活地启用或禁用特定的监控组件。

interface InstallableWatcher {

 fun install()

 fun uninstall()
}

ActivityWatcher在install时,监听了 Application.ActivityLifecycleCallbacks

class ActivityWatcher(
  private val application: Application,
  private val deletableObjectReporter: DeletableObjectReporter
) : InstallableWatcher {

  // Kept for backward compatibility.
  constructor(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher
  ) : this(application, reachabilityWatcher.asDeletableObjectReporter())

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        deletableObjectReporter.expectDeletionFor(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

  override fun install() {
    // 监听 lifecycleCallbacks
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

  override fun uninstall() {
    // 取消监听
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
}

其它3个 FragmentAndViewModelWatcher、ServiceWatcher、RootViewWatcher 原理类似:

  • 监听 Fragment 的 onDestroy() 和 onDestroyView() 回调
  • 监听 AndroidX ViewModel 的 onCleared() 回调
  • 监听 Service 的 onDestroy() 回调
  • 监听 View 的 onDetachedFromWindow() 回调

4个Watcher 和 ObjectWatcher

可以看到在onDestroy 里面并没有直接调用到 ObjectWatcher

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        // ObjectWatcher 实现了 ReachabilityWatcher 接口
        deletableObjectReporter.expectDeletionFor(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

而是使用了一种适配器模式,通过接口来调用, 这样可以让其他 Watcher 不直接依赖 ObjectWatcher,而是依赖更抽象的 DeletableObjectReporter 接口。

fun interface ReachabilityWatcher {


  fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  )

  fun asDeletableObjectReporter(): DeletableObjectReporter =
    DeletableObjectReporter { target, reason ->
      expectWeaklyReachable(target, reason)
    }
}

为什么要使用适配器模式?

这样可以给 ActivityWatcher 传入不同的 DeletableObjectReporter。例如默认的 ObjectWatcher、带延迟的包装器、过滤的包装器等等。

  ActivityWatcher(application, defaultReporter)
  ActivityWatcher(application, delayedReporter)
  ActivityWatcher(application, filteredReporter)

这其实也更符合软件设计的 依赖倒置原则

  原则内容:
  - 高层模块不应依赖于低层模块,二者都应依赖于抽象
  - 抽象不应依赖于细节,细节应依赖于抽象

  LeakCanary 中的应用:
  // 高层模块(ActivityWatcher)依赖抽象接口
  class ActivityWatcher(
      private val application: Application,
      private val deletableObjectReporter: DeletableObjectReporter  // 依赖抽象
  ) : InstallableWatcher

  // 低层模块(ObjectWatcher)实现抽象接口
  class ObjectWatcher : ReachabilityWatcher, RetainedObjectTracker {
      // 实现细节
  }

ObjectWatcher 的核心实现

class ObjectWatcher private constructor(
  private val checkRetainedExecutor: Executor,
  private val clock: Clock,
  private val isEnabled: () -> Boolean,
  private val onObjectRetainedListeners: MutableSet<OnObjectRetainedListener> = CopyOnWriteArraySet(),
  private val retainedObjectTracker: ReferenceQueueRetainedObjectTracker = ReferenceQueueRetainedObjectTracker(
    { clock.uptimeMillis().milliseconds }) {
    onObjectRetainedListeners.forEach { it.onObjectRetained() }
  },
  // 🌟使用委托模式 (by retainedObjectTracker) 将核心逻辑委托给 ReferenceQueueRetainedObjectTracker
) : RetainedObjectTracker by retainedObjectTracker, ReachabilityWatcher {

  constructor(
    clock: Clock,
    checkRetainedExecutor: Executor,
    /**
     * Calls to [] will be ignored when [isEnabled] returns false
     */
    isEnabled: () -> Boolean = { true },
  ) : this(
    checkRetainedExecutor, clock, isEnabled
  )

  /**
   * Returns true if there are watched objects that aren't weakly reachable, even
   * if they haven't been watched for long enough to be considered retained.
   */
  val hasWatchedObjects: Boolean
    get() = hasTrackedObjects

  /**
   * Returns the objects that are currently considered retained. Calling this method will
   * end up creating local references to the objects, preventing them from becoming weakly
   * reachable, and creating a leak.
   */
  val retainedObjects: List<Any>
    get() = retainedObjectTracker.retainedWeakReferences.mapNotNull { it.getAndLeakReferent() }

  fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) {
    onObjectRetainedListeners.add(listener)
  }

  fun removeOnObjectRetainedListener(listener: OnObjectRetainedListener) {
    onObjectRetainedListeners.remove(listener)
  }

  /**
   * Identical to [watch] with an empty string reference name.
   */
  fun watch(watchedObject: Any) {
    expectWeaklyReachable(watchedObject, "unknown: reason not provided")
  }

  fun watch(
    watchedObject: Any,
    description: String
  ) {
    expectWeaklyReachable(watchedObject, description)
  }

  // 核心方法 
  override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
     // 1. 创建触发器
    val retainTrigger =
      retainedObjectTracker.expectDeletionOnTriggerFor(watchedObject, description)
     // 2. 延迟检查
    checkRetainedExecutor.execute {
      retainTrigger.markRetainedIfStronglyReachable()
    }
  }

  /**
   * Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on
   * [clock] [Clock.uptimeMillis])
   */
  fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
    clearObjectsTrackedBefore(heapDumpUptimeMillis.milliseconds)
  }

  /**
   * Clears all [KeyedWeakReference]
   */
  fun clearWatchedObjects() {
    clearAllObjectsTracked()
  }
}

可以看到关键方法在于 expectDeletionOnTriggerFor 这个方法,刚才说到核心逻辑其实是委托给了 ReferenceQueueRetainedObjectTracker,我们可以直接查看 ReferenceQueueRetainedObjectTracker 的 expectDeletionOnTriggerFor 方法


class ReferenceQueueRetainedObjectTracker constructor(
  private val clock: UptimeClock,
  private val onObjectRetainedListener: OnObjectRetainedListener
) : RetainedObjectTracker, TriggeredDeletableObjectReporter {

  private val watchedObjects: MutableMap<String, KeyedWeakReference> = ConcurrentHashMap()

  private val queue = ReferenceQueue<Any>() // 引用队列

  val trackedWeakReferences: List<KeyedWeakReference>  // 被监控的弱引用列表

  val retainedWeakReferences: List<KeyedWeakReference> // 被标记为泄漏的弱引用列表

  override val hasRetainedObjects: Boolean

  override val retainedObjectCount: Int

  override val hasTrackedObjects: Boolean

  override val trackedObjectCount: Int

  //  开始监控一个对象,期望它能被回收
  override fun expectDeletionOnTriggerFor(
    target: Any,
    reason: String
  ): RetainTrigger {
    // 1. 清理已被回收的对象
    removeWeaklyReachableObjects()
     // 2. 生成唯一标识符
    val key = UUID.randomUUID()
      .toString()
    val watchUptime = clock.uptime()
     // 3. 创建弱引用包装
    val reference =
      KeyedWeakReference(target, key, reason, watchUptime.inWholeMilliseconds, queue)
   // 4. 存储到监控列表
    watchedObjects[key] = reference
     // 5. 返回触发器
    return object : RetainTrigger {
      override val isStronglyReachable: Boolean

      override val isRetained: Boolean

      override fun markRetainedIfStronglyReachable() {
        moveToRetained(key)
      }
    }
  }

  override fun clearObjectsTrackedBefore(uptime: Duration) {
    val weakRefsToRemove =
      watchedObjects.filter { it.value.watchUptimeMillis <= uptime.inWholeMilliseconds }
    weakRefsToRemove.values.forEach { it.clear() }
    watchedObjects.keys.removeAll(weakRefsToRemove.keys)
  }


  override fun clearAllObjectsTracked() {
    watchedObjects.values.forEach { it.clear() }
    watchedObjects.clear()
  }

  // 将指定对象标记为泄漏
  private fun moveToRetained(key: String) {
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptime().inWholeMilliseconds
      onObjectRetainedListener.onObjectRetained()
    }
  }

  // removeWeaklyReachableObjects()
  private fun removeWeaklyReachableObjects() {

    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }
}

整体流程图

  Activity.onDestroy()
      ↓
  ActivityWatcher.onActivityDestroyed()
      ↓
  deletableObjectReporter.expectDeletionFor()
      ↓
  ObjectWatcher.expectWeaklyReachable()
      ↓
  ReferenceQueueRetainedObjectTracker.expectDeletionOnTriggerFor()
      ↓
  创建 KeyedWeakReference 并存储到 watchedObjects
      ↓
  延迟 5 秒执行检查
      ↓
  RetainTrigger.markRetainedIfStronglyReachable()
      ↓
  moveToRetained()
      ↓
  检查对象是否仍在 watchedObjects 中
      ↓
  如果存在:标记为泄漏,通知监听器
  如果不存在:已被正常回收