LeakCanary 源码分析 #1

何言 2021年12月03日 882次浏览

LeakCanary 是一个检查内存泄漏的库。以下是官网中的定义:

LeakCanary is a memory leak detection library for Android.

相关网站:

LeakCanary (square.github.io)

square/leakcanary: A memory leak detection library for Android. (github.com)

官网描述

我们先来看看官网中的一些描述

Getting started

我们只需要将 leakcanary-android 依赖添加到你的 build.gradle 中:

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  // 使用 debugImplementation ,只在 debug 环境引入
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}

完成了,不需要改变你的任何代码。

可以通过 Logcat 中 tag 为 LeakCanary 的日志来确保 LeakCnary 启动:

D LeakCanary: LeakCanary is running and ready to detect leaks

LeakCanary 会自动检测以下对象的内存泄漏:

  • 被销毁的 Activity 实例
  • 被销毁的 Fragment 实例
  • 被销毁的 fragment 的 View 实例
  • 被销毁的 viewModel 实例

How LeakCanary works

LeakCanary 被安装后,会自动检测和报告内存泄漏,具体来说会执行以下四个步骤:

  1. 检测 被保留 的对象(没有被 GC 回收)
  2. 生成堆内存快照 Heap dump
  3. 分析堆内存
  4. 为泄露分类(判断属于哪一种类型)

1. 检测保留对象

LeakCanary 通过 hook android 中的 lifecycle 对象来自动判断 activity 与 fragment 应该被 GC 的时候。这些需要被摧毁的对象(activity 与 fragment 等)会传入一个 ObjectWatcher,Watcher 会持有该对象的 弱引用来判断是否被回收 。LeakCanary 会自动检测以上说明的支持自动检测的实例,同时你还可以使用 ObjectWatcher 来检测自定义的已经不需要的实例:

AppWatcher.objectWatcher.watch(myDetachedView, "View was detached")

如果 objectWatcher 持有的弱引用在 5 秒并且执行了一次 GC 之后还存在,则认为该对象为 保留对象。同时会打出 Log:

D LeakCanary: Watching instance of com.example.leakcanary.MainActivity
  (Activity received Activity#onDestroy() callback) 

... 5 seconds later ...

D LeakCanary: Scheduling check for retained objects because found new object
  retained

如果保留对象的数量达到了一个阈值,则会生成对内存快照进行分析。同时等待时会显示带有当前保留对象数量与阈值的通知。

同时可能会出现该 Log:

D LeakCanary: Rescheduling check for retained objects in 2000ms because found
  only 4 retained objects (< 5 while app visible)

(Log 中表示在 2000 ms 内保留对象没达到阈值则会重新进行保留对象分析)

2. 生成堆内存快照

当保留对象达到阈值时 LeakCanary 会生成一个堆内存快照,会在安卓的 文件系统 中储存一个 .hprof 文件。

默认会存储到 应用程序目录下( android/data/包名 ),如果已经授予了 WRITE_EXTERNAL_STORAGE 权限则会储存在 储存卡/leakcanary-[包名] 目录下。如果需要运行时动态申请,则会弹出一个请求通知,可以通过点击通知运行时授予权限。

生成 堆内存快照 会短暂冻结软件,同时会弹出一个 Toast

3. 分析对内存快照

LeakCanary 会使用 Shark 模块来分析 .hprof 文件,定位保留对象的位置。LeakCanary 会找到每一个保留对象的引用路径。

获取到引用路径后,LeakCanary 会对内存泄漏的原因进行分析,并生成其签名,同时按照其签名进行分组。

分组结束后会弹出通知说明有多少个保留对象被分为多少个组,同时在 Logcat 中打印更具体的信息。

你可以点击通知或点击桌面图标(会自动生成)来在设备上查看到具体的保留对象、标签与其引用路径。

例如以下泄露情况:

...
│  
├─ com.example.leakcanary.LeakingSingleton class
│    Leaking: NO (a class is never leaking)
│    ↓ static LeakingSingleton.leakedViews
│                              ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    Leaking: UNKNOWN
│    ↓ ArrayList.elementData
│                ~~~~~~~~~~~
├─ java.lang.Object[] array
│    Leaking: UNKNOWN
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
...

其签名生成方式:

val leakSignature = sha1Hash(
    "com.example.leakcanary.LeakingSingleton.leakedView" +
    "java.util.ArrayList.elementData" +
    "java.lang.Object[].[x]"
)
println(leakSignature)
// dbfa277d7e5624792e8b60bc950cd164190a11aa

4. 为内存泄漏分类

WeakCanary 会将 内存泄漏 分为两类,其中一类为 Application Leaks,为项目源码中的代码的内存泄漏,第二类为 Library Leaks,为该项目引用的第三方库中的内存泄漏。第三方库中的内存泄漏可能不在该项目开发者处理的范畴,因此将其独立出来。

Module 架构

  • leakcanary-android:入口 Module,我们使用一般就依赖该 Module,在由该 Module 依赖其他 模块。同时该 Module 中还有一个 AndroidManifest.xml 文件,其中注册了一个 用于分析内存泄漏的 Service 。

    <application>
        <service
            android:name="leakcanary.internal.HeapAnalyzerService"
            android:exported="false" />
      </application>
    
  • leakcanary-android-core:核心代码,包括显示内存泄漏信息的页面,通知等代码。

  • leakcanary-object-watcher:观察者相关代码,核心为一个 Watcher 对象,之前例子中的 objectWatcher 对象。

  • leakcanary-object-watcher-android、leakcanary-object-watcher-androidx、leakcanary-object-watcher-android-support-fragment:各种支持自动分析内存泄漏的对象的 watcher 对象。

  • shark:各种分析模块的父 Module

  • shark-android:分析安卓设备,包括型号安卓版本等信息

  • shark-hprof:分析 .hprof 文件

  • shark-graph:分析图

  • 其他 sharkx-xxx:各种分析模块

  • plumber-android:自动修复工具,对于已知的内存泄漏问题,尝试在进行时进行自动修复(例如通过反射直接将其置空),包括 Library Weak,目前只收录了十几个已知问题。

还有一些其他 Module,其中有一部分代码,但难以总结该 Module 的作用,这里不给出。

接下来开始分析代码。

Object-Watcher 保留对象观察

对象观察者,首先就是 ObjectWatcher 对象。首先是构造方法:

class ObjectWatcher constructor(
  private val clock: Clock, // 时钟,确保时间一致
  private val checkRetainedExecutor: Executor, // 线程池,用于等待检测是否可达的事件
  /**
   * Calls to [watch] will be ignored when [isEnabled] returns false
   */
  private val isEnabled: () -> Boolean = { true } // 如果返回 false,则会忽略对 watch 的调用(关闭功能)
) : ReachabilityWatcher {
    // ……
}

关于观察的方法,LeakCanary 使用了 ReachabilityWatcher 接口进行定义:

fun interface ReachabilityWatcher {

  /**
   * Expects the provided [watchedObject] to become weakly reachable soon. If not,
   * [watchedObject] will be considered retained.
   */
  fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  )
}

调用该方法说明我们传入的 watchedObject 很快将会变得弱可达(Watcher 本身也会持有弱引用),如果没有,将会把该对象识别为保留对象。

也就是说调用该方法 LeakCanary 将会在接下来一段时间后判断该对象是否弱可达。我们回到 ObjectWatcher,看看该方法的具体实现:

  @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
      // 是否开启,如果关闭则不作任何处理
    if (!isEnabled()) {
      return
    }
      
      // 删除该 Watcher 观察的所有已经处于弱可达的对象
    removeWeaklyReachableObjects()
      
      // UUID
    val key = UUID.randomUUID()
      .toString()
      
      // 获取时间
    val watchUptimeMillis = clock.uptimeMillis()
      
      // 构造 KeyedWeakReference 对象
    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 容器中
    watchedObjects[key] = reference
      
      // 使用 checkRetainedExecutor 添加检查方法
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

先来看看 KeyedWeakReference 对象:

class KeyedWeakReference(
  referent: Any,
  val key: String,
  val description: String,
  val watchUptimeMillis: Long,
  referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>( // 继承 WeakReference
  referent, referenceQueue
) {
  /**
   * Time at which the associated object ([referent]) was considered retained, or -1 if it hasn't
   * been yet.
   */
    // 该对象被视为保留对象的时间,如果为 -1 这说明当前不是保留对象
  @Volatile
  var retainedUptimeMillis = -1L

  override fun clear() {
    super.clear()
    retainedUptimeMillis = -1L
  }

  companion object {
      // 获取堆快照的开始时间
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L
  }
}

可以看到就是将 key、description 与 watchUptimeMillis 信息包装进 WeakReference 对象。

该对象传入了一个 ReferenceQueue<Any> 对象,在弱引用的对象被回收后,将会在该队列中加入弱引用对象。

接下来我们来到 removeWeaklyReachableObjects 方法:

// 将已经被回收的弱引用对象从 watchedObjects 中删除  
private fun removeWeaklyReachableObjects() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    var ref: KeyedWeakReference?
    do {
        // 如果队列中的对象为 KeyedWeakReference
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
          // 删除对象
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }

该方法就是将已经回收的对象从观察对象中删除,可以看做刷新当前的 watchedObjects 容器。

然后来到 moveToRetained:

  @Synchronized private fun moveToRetained(key: String) {
      // 在调用一次 removeWeaklyReachableObjects 刷新容器
    removeWeaklyReachableObjects()
      
      // 获取对应的 引用
    val retainedRef = watchedObjects[key]
      
    if (retainedRef != null) {
        // 如果没被回收,则会看做保留对象,这里调用一下回调和更新时间变量
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }

可以看到这里没有涉及延时操作,实际上,延时是通过传入的 Executor 控制的。

例如我们来到 AppWatcher,这是针对安卓的一个单例模式类,其中就构造了一个 ObjectWatcher 对象:

  val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
      
      // 通过 executor 达到延时
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
        // 使用 handler 达到延迟的效果
      mainHandler.postDelayed(it, retainedDelayMillis)
    },
    isEnabled = { true }
  )

Object-Watcher-Android 使用 ContentProvider 自动启动

首先来到 AndroidManifest.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.squareup.leakcanary.objectwatcher"
    >

  <application>
    <provider
        android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
        android:authorities="${applicationId}.leakcanary-installer"
        android:enabled="@bool/leak_canary_watcher_auto_install"
        android:exported="false"/>
  </application>
</manifest>

可以看到这里注册了一个 ContentProvider,这里也是 LeakCanary 能不改变任何代码,直接导入就可以使用的原因,可以看到这个 ContentProvider 的位置为 leakcanary.internal.AppWatcherInstaller$MainProcess,值得注意的是这里有一个 enabled 字段,引用了 res/values 中的一个字段 leak_canary_watcher_auto_install,代表是否要自动启用。

回到这个 Provider, 是一个内部类,代码如下:

internal sealed class AppWatcherInstaller : ContentProvider() {

  // 自动安装的运行在主进程内部类
  internal class MainProcess : AppWatcherInstaller()
    
    // 运行在其他进程的
  internal class LeakCanaryProcess : AppWatcherInstaller()

    // 程序启动时会执行
  override fun onCreate(): Boolean {
      
      // 获取 Application
    val application = context!!.applicationContext as Application
      // 调用 AppWatcher.manualInstall 方法
    AppWatcher.manualInstall(application)
    return true
  }

  override fun query(
    uri: Uri,
    strings: Array<String>?,
    s: String?,
    strings1: Array<String>?,
    s1: String?
  ): Cursor? {
    return null
  }

  override fun getType(uri: Uri): String? {
    return null
  }

  override fun insert(
    uri: Uri,
    contentValues: ContentValues?
  ): Uri? {
    return null
  }

  override fun delete(
    uri: Uri,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }

  override fun update(
    uri: Uri,
    contentValues: ContentValues?,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }
}

可以看到在 onCreate 里调用了 AppWatcher 的 manualInstall 方法,让我们来看看该对象:

image20211203191537027.png

首先是一个 Object 单例模式,然后有三个变量,其中一个为 retainedDelayMillis,表示延迟判断是否回收的时间,还有一个是 installCause ,类型为异常,用于判断是否安装,这里如果安装之后不为 null,有一个自定义异常,为 null 说明未安装,然后就是 objectWatcher:

val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
      mainHandler.postDelayed(it, retainedDelayMillis)
    },
    isEnabled = { true }
  )

这里与之前一样,不做展开。

我们重点来到 manualInstall 方法:

  @JvmOverloads
  fun manualInstall(
      // Application
    application: Application, 
      // 延迟判断保留对象时间
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
      // 要观察的的对象,默认会调用 appDefaultWatchers 方法
    watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
  ) {
      // 在主线程执行
    checkMainThread()
      
      // 只能安装一次
    if (isInstalled) {
      throw IllegalStateException(
        "AppWatcher already installed, see exception cause for prior install call", installCause
      )
    }
      
      // 延迟时间要大于等于 0
    check(retainedDelayMillis >= 0) {
      "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
    }
      // 已经安装好
    installCause = RuntimeException("manualInstall() first called here")
      
      // 延迟时间
    this.retainedDelayMillis = retainedDelayMillis
      
      // 如果是 debug 环境,则打印 Log
    if (application.isDebuggableBuild) {
      LogcatSharkLog.install()
    }
    // Requires AppWatcher.objectWatcher to be set
      // (1)
    LeakCanaryDelegate.loadLeakCanary(application)

      // (2)
    watchersToInstall.forEach {
      it.install()
    }
  

这里重点在后面的 LeakCanaryDelegate.loadLeakCanary(application),与最后的循环,让我们先来看这个循环,其中默认情况下 watchersToInstall 的值为调用 appDefaultWatchers(application) 获取:

  fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
        // Activity
      ActivityWatcher(application, reachabilityWatcher),
        // Fragment 和 ViewModel
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
        // 跟 View
      RootViewWatcher(reachabilityWatcher),
        // Service
      ServiceWatcher(reachabilityWatcher)
    )
  }

可以看到默认有四个 InstallableWatcher,这里只分析第一个:

class ActivityWatcher(
    // Application
  private val application: Application,
    // ReachbilityWatcher ,实际上就是 ObjectWatcher 对象
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() { // 使用委托,委托给了 noOpDelegate,已提供拓展性
        // 在 Activity onDestroy 时使用 ObjectWatcher 来观察
      override fun onActivityDestroyed(activity: Activity) {
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

    // 注册生命周期监听器
  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
}

然后我们来到 委托,这里有两个地方有委托:

首先是 ActivityWatcher 中的 ActivityLifecycleCallbacks,使用了委托:

private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
        // 委托给 noOpDelegate()
      override fun onActivityDestroyed(activity: Activity) {
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

这里的 noOpDelegate() 方法:

internal inline fun <reified T : Any> noOpDelegate(): T = leakcanary.internal.noOpDelegate()

这里的 noOpDelegate() 方法:

internal inline fun <reified T : Any> noOpDelegate(): T {
  val javaClass = T::class.java
  return Proxy.newProxyInstance(
    javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER
  ) as T
}

private val NO_OP_HANDLER = InvocationHandler { _, _, _ ->
  // no op
}

这里相当于实现了 ActivityLifecycleCallbacks 的所有方法,但是其他方法都是空实现,然后再次重写了 onActivityDestroyed,个人理解是增加了扩展性,我们可以在 NO_OP_HANDLER 中写出其他方法。

其次是 AppWatcher 的 manualInstall 方法中的

LeakCanaryDelegate.loadLeakCanary(application)

这里的 LeakCanaryDelegate 为一个单例模式:

internal object LeakCanaryDelegate {

  @Suppress("UNCHECKED_CAST")
  val loadLeakCanary by lazy {
    try {
        // 通过反射获取 leakcanary.internal.InternalLeakCanary 中的对象
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null) as (Application) -> Unit
    } catch (ignored: Throwable) {
      NoLeakCanary
    }
  }

  object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    override fun invoke(application: Application) {
    }

    override fun onObjectRetained() {
    }
  }
}

这里通过反射获取 leakcanary.internal.InternalLeakCanary 中的对象,然后强转成 (Application) -> Unit 接口。

当我们引用这个包之后,只要自己写一个该对象,则可自动执行,

Android-Core 安卓各组件的保留对象分析

实际上,在 Android-Core 中该地方就有一个对象,该对象代码比较多,这里只给出部分:

package leakcanary.internal

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    
    override fun invoke(application: Application) {
        
        // 注册对象视为保留对象的回调
        AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

        // 初始化 保存堆内存快照的工具类
        val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))

        // 初始化 GC 触发器
        val gcTrigger = GcTrigger.Default
        
        //……
        
    }
    
    // 将对象视为保留对象之后会执行
      override fun onObjectRetained() = scheduleRetainedObjectCheck()

    
      fun scheduleRetainedObjectCheck() {
          
          // 调用 转存堆 工具类的代码
        if (this::heapDumpTrigger.isInitialized) {
          heapDumpTrigger.scheduleRetainedObjectCheck()
        }
      }
    
}

最后,当我们观察的对象超过时间后还没有被回收时,就会调用其中的 onObjectRetained,最终来到 heapDumpTrigger.scheduleRetainedObjectCheck(),这里也就是判断是否内存泄露的最后一个方法,在该方法里,如果泄露数量大于阈值,则会开始保存快照:

  fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      return
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
      
      // 在后台执行,并延迟
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
        // 执行方法
      checkRetainedObjects()
    }, delayMillis)
  }

最终来到 checkRetainedObjects() 方法:(只给出部分代码,剩下设计通知以及配置文件等的判断和分析)

  private fun checkRetainedObjects() {
      var retainedReferenceCount = objectWatcher.retainedObjectCount

      // ……
      
      // 如果有保留对象,则手动触发 GC
      if (retainedReferenceCount > 0) {
          gcTrigger.runGc()
          // 刷新 Count
          retainedReferenceCount = objectWatcher.retainedObjectCount
      }

      // 判断阈值
      if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

      val now = SystemClock.uptimeMillis()
      val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
      if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
          onRetainInstanceListener.onEvent(DumpHappenedRecently)
          showRetainedCountNotification(
              objectCount = retainedReferenceCount,
              contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
          )
          scheduleRetainedObjectCheck(
              delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
          )
          return
      }

      dismissRetainedCountNotification()
      val visibility = if (applicationVisible) "visible" else "not visible"
      
      // 生成快照,并进行分析
      dumpHeap(
          retainedReferenceCount = retainedReferenceCount,
          retry = true,
          reason = "$retainedReferenceCount retained objects, app is $visibility"
      )
  }

然后来到 dumpHeap 方法

private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean,
    reason: String
  ){
    
    //…… 
    // 生成堆内存 .href 文件
    // 最终会调用 Debug.dumpHprofData(path) 方法生成 .href 文件
    // ………
    
    // 分析 .href 文件
    HeapAnalyzerService.runAnalysis(
          context = application,
          heapDumpFile = heapDumpResult.file,
          heapDumpDurationMillis = heapDumpResult.durationMillis,
          heapDumpReason = reason
        )
}

这里生成 .href 文件的过程省去,最终来到 HeapAnalyzerService.runAnalysis 方法,首先 HeapAnalyzerService 为一个 Service,其中通过伴生对象定义了一个 runAnalysis 方法,如下:

internal class HeapAnalyzerService : ForegroundService(
  HeapAnalyzerService::class.java.simpleName,
  R.string.leak_canary_notification_analysing,
  R.id.leak_canary_notification_analyzing_heap
), OnAnalysisProgressListener {
    
    companion object {
        
        fun runAnalysis(
          context: Context,
          heapDumpFile: File,
          heapDumpDurationMillis: Long? = null,
          heapDumpReason: String = "Unknown"
        ) {
          val intent = Intent(context, HeapAnalyzerService::class.java)
          intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
          intent.putExtra(HEAPDUMP_REASON_EXTRA, heapDumpReason)
          heapDumpDurationMillis?.let {
            intent.putExtra(HEAPDUMP_DURATION_MILLIS_EXTRA, heapDumpDurationMillis)
          }
            // 启动该 Service
          startForegroundService(context, intent)
        }
    }

}
    

启动后,对调用其中的 onHandleIntentInForeground 方法,进而调用 analyzeHeap 方法:

  private fun analyzeHeap(
    heapDumpFile: File,
    config: Config
  ): HeapAnalysis {
      
      // 构造一个 HeapAnalyzer 对象
    val heapAnalyzer = HeapAnalyzer(this)

    val proguardMappingReader = try {
      ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
    } catch (e: IOException) {
      null
    }
      
      // 开始分析堆内存文件
    return heapAnalyzer.analyze(
      heapDumpFile = heapDumpFile,
      leakingObjectFinder = config.leakingObjectFinder,
      referenceMatchers = config.referenceMatchers,
      computeRetainedHeapSize = config.computeRetainedHeapSize,
      objectInspectors = config.objectInspectors,
      metadataExtractor = config.metadataExtractor,
      proguardMapping = proguardMappingReader?.readProguardMapping()
    )
  }

HeapAnalyzer 为 Shark 这个 Module 中的代码,实际上 Shark 就是 LeakCanary 开源的一个 href 文件分析器,其中使用了图这种数据结构进行储存,并使用广度优先搜索寻找泄露对象的持有链。关于 LeakCanary 的分析暂时到这里,下一篇将仔细分析 Shark 这个 Module。