源码阅读 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.MainProcessAppWatcherInstaller 的 onCreate:

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 保存 UUID 为key的弱引用对象
  3. queue 为 ReferenceQueue, 用于GC回收时,弱引用会自动进入队列,也就是 未泄漏。
  4. checkRetainedExecutor 是 Executor,默认实现 延迟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

这里会 触发 InternalLeakCanary 的 onObjectRetainer(),进一步 处理。

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)
  }
}

文档

相关推荐
驰骋工作流2 小时前
流程时效考核的表结构:从待办时限到 WF_CH 考核结果驰骋BPM驰骋低代码低代码工作流引擎
android·低代码·rxjava
传奇开心果编程4 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
方白羽8 小时前
Android 双开环境底层剖析与风控检测落地
android·安全·app
weixin_563371478 小时前
Android Studio 2024 版 Device Explorer 打开方法
android·ide·android studio
AIsoft_868911 小时前
职场音频文件怎么整理?录音APP有哪些用法
android·音视频
booboosui11 小时前
第一批个人 Agent 用户正在卸载,问题不在模型,在它住在哪儿
android
洞见前行11 小时前
点击图标到内容出现,你的 App 在这几秒里做了什么:Android 冷启动全链路优化实战
android·性能优化·android studio
凯基迪科技11 小时前
芯片UID绑定修改 加密与逆向修改思路分析
android
Liben12 小时前
Weex开发踩坑记录
android·前端·ios
救救孩子把14 小时前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务