源码阅读 LeakCanary

LeakCanary源码分析

LeakCanary作为常用的内存泄漏分析工具,那是如何检测内存泄漏的呢?从注册入口分析,最新的LeakCanary 2.14,通过ContentProvider自动获取Context完成注册。

xml 复制代码
<provider
    android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
    android:authorities="${applicationId}.leakcanary-installer"
    android:enabled="@bool/leak_canary_watcher_auto_install"
    android:exported="false" />

一、MainProcessAppWatcherInstaller

leakcanary.internal.MainProcessAppWatcherInstalleronCreate

kotlin 复制代码
internal class MainProcessAppWatcherInstaller : ContentProvider() {

  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }
  ...
}

二、AppWatcher.manualInstall

manualInstall 中 完成 安装:

kotlin 复制代码
// leakcanary.internal.InternalLeakCanary 内部实现
LeakCanaryDelegate.loadLeakCanary(application)

// 相关 Activity/Fragment 组件的 检测类,都初始化
watchersToInstall.forEach {
    it.install()
}
2.1 appDefaultWatchers

watchersToInstall 默认由 AppWatcher.appDefaultWatchers 创建 组件的内存检查对象。

他们都实现 InstallableWatcher 接口,实现 install()uninstall() 方法。

kotlin 复制代码
  fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
      ActivityWatcher(application, reachabilityWatcher),
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      RootViewWatcher(reachabilityWatcher),
      ServiceWatcher(reachabilityWatcher)
    )
  }
2.2 ActivityWatcher - Activity的泄漏分析

Activity 利用生命周期函数 检测。

  1. 利用 Application.registerActivityLifecycleCallbacks() 监听
  2. onActivityDestroyed()时,调用 objectWatcher.expectWeaklyReachable() 进行 弱可达性分析

三、ObjectWatcher

ObjectWatcher 完成 内存检查对象的 弱引用创建 和 GC处理。

所有 Activity Fragment View 检测的 InstallableWatcher 都有 objectWatcher 成员,

需要检查泄漏时,来调用 expectWeaklyReachable 方法。

3.1 AppWatcher.objectWatcher 创建默认值
kotlin 复制代码
  val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
      mainHandler.postDelayed(it, retainedDelayMillis)
    },
    isEnabled = { true }
  )

解释: checkRetainedExecutor 作为 Executor 实现,延迟 5s 在 mainHandler 主线程完成。

3.2 expectWeaklyReachable 检查方法
  • KeyedWeakReference
  • watchedObjects[key]
kotlin 复制代码
  @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
    removeWeaklyReachableObjects()
    val key = UUID.randomUUID()
      .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    SharkLog.d {
      "Watching " +
        (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
        (if (description.isNotEmpty()) " ($description)" else "") +
        " with key $key"
    }

    watchedObjects[key] = reference
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

相关字段说明:

  1. KeyedWeakReference 继承自 WeakReference,增加了 key 用于判断。
  2. watchedObjects 保存 UUIDkey的弱引用对象
  3. queueReferenceQueue, 用于GC回收时,弱引用会自动进入队列,也就是 未泄漏。
  4. checkRetainedExecutorExecutor,默认实现 延迟5s 执行。
3.3 removeWeaklyReachableObjects

queue中是 准备被回收的 Reference

循环 queue.poll() 获取,并移除 要回收 的引用对象。

kotlin 复制代码
  private fun removeWeaklyReachableObjects() {
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }
3.4 moveToRetained

这里会 触发 InternalLeakCanaryonObjectRetainer(),进一步 处理。

kotlin 复制代码
  @Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }

四、InternalLeakCanary

回到 AppWatcher.manualInstall() 方法,对应:

kotlin 复制代码
LeakCanaryDelegate.loadLeakCanary(application)

会通过反射 创建 InternalLeakCanary对象, 并执行 InternalLeakCanary.invoke 方法,注册了上面的OnObjectRetainedListener监听器:

kotlin 复制代码
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
4.1 onObjectRetained

--> scheduleRetainedObjectCheck()

--> heapDumpTrigger.scheduleRetainedObjectCheck()

--> heapDumpTrigger.checkRetainedObjects()

checkRetainedObjects() 源码:

kotlin 复制代码
    ...
    var retainedReferenceCount = objectWatcher.retainedObjectCount

    if (retainedReferenceCount > 0) {
      gcTrigger.runGc()
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }

    ...
    dumpHeap(
      retainedReferenceCount = retainedReferenceCount,
      retry = true,
      reason = "$retainedReferenceCount retained objects, app is $visibility"
    )

这里关键地方:

  1. gcTrigger.runGc() 运行GC,检测泄漏对象数量。
  2. heapDump() 方法,保存栈信息,本质是 Android的 Debug.dumpHprofData()

看源码:HeapDumpTrigger.kt

4.2 GcTrigger

通过 Runtime.getRuntime().gc() 进行 GC 操作。

kotlin 复制代码
  object Default : GcTrigger {
    override fun runGc() {
      Runtime.getRuntime()
        .gc()
      enqueueReferences()
      System.runFinalization()
    }
    ...
  }

源码:GcTrigger.kt

4.3 AndroidDebugHeapDumper

利用 Android Api Debug.dumpHprofData(path) 获取栈信息:

kotlin 复制代码
object AndroidDebugHeapDumper : HeapDumper {
  override fun dumpHeap(heapDumpFile: File) {
    Debug.dumpHprofData(heapDumpFile.absolutePath)
  }
}

文档

相关推荐
游戏开发爱好者81 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
王码码20351 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
黑码哥2 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
亓才孓2 小时前
[JDBC]元数据
android
独行soc2 小时前
2026年渗透测试面试题总结-17(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
金融RPA机器人丨实在智能2 小时前
Android Studio开发App项目进入AI深水区:实在智能Agent引领无代码交互革命
android·人工智能·ai·android studio
科技块儿2 小时前
利用IP查询在智慧城市交通信号系统中的应用探索
android·tcp/ip·智慧城市
独行soc3 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
王码码20353 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
2501_915106323 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview