一 LeakCanary 简介

LeakCanary 是一款 Android 渠道上进行内存走漏检测的东西,其简介及运用办法可参阅 LeakCanary 2.0 作业原理及运用详解 。本文主要从源码角度来剖析其作业流程。

二 源码剖析

LeakCanary 作业流程大致可分为以下 6 个阶段:

  1. 初始化: 初始化 LeakCanary 内部剖析引擎
  2. 注册废物目标的监听: 在 Android Framework 中注册监听器,感知五种 Android 内存走漏场景中产生废物目标的机遇
  3. 监控内存走漏: 为废物目标相关弱引证目标,若一段时刻后引证目标没有按预期进入引证行列,则以为目标产生内存走漏
  4. Java Heap Dump: 当走漏目标计数到达阈值时,会触发 Java Heap Dump 并生成.hprof 文件存储到文件体系中
  5. 剖析堆快照: 运用 Shark 剖析.hprof文件
  6. 输出剖析陈述: 剖析作业完结后,会在 Logcat 打印剖析结果,也会发送一条体系告诉音讯

2.1 初始化

旧版本的 LeakCanary 需求在 Application 中调用相关初始化 API,而在 LeakCanary2.0 利用了 ContentProvider 的发动机制来间接调用初始化 API, 完结了无侵入的LeakCanary初始化。

在项目工程 leakcanary-object-watcher-androidAndroidManifext.xml文件中,注册了一个承继自 ContentProvider 的MainProcessAppWatcherInstaller。运用发动时,会先调用注册的 ContentProvider 的 onCreate 完结初始化

AndroidManifext.xml

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

在 MainProcessAppWatcherInstaller 类的 onCreate 办法中,实际是 AppWatcher.manualInstall(application) 完结了 LeakCanary 的初始化。
MainProcessAppWatcherInstaller.kt

internal class MainProcessAppWatcherInstaller : ContentProvider() {
  override fun onCreate(): Boolean {
    // 初始化 LeakCanary
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }
  ...
}

AppWatcher.kt

@JvmOverloads
fun manualInstall(
 application: Application,
 retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
 watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
 // 保证在主线程,不然抛出 UnsupportedOperationException 异常
 checkMainThread()
 if (isInstalled) {
  throw IllegalStateException(
   "AppWatcher already installed, see exception cause for prior install call", installCause
  )
 }
 check(retainedDelayMillis >= 0) {
  "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
 }
 this.retainedDelayMillis = retainedDelayMillis
 if (application.isDebuggableBuild) {
  LogcatSharkLog.install()
 }
 // 初始化 InternalLeakCanary 内部引擎
 LeakCanaryDelegate.loadLeakCanary(application)
 // 注册四种 Android 走漏场景的监控 Hook 点
 watchersToInstall.forEach {
  it.install()
 }
 // Only install after we're fully done with init.
 installCause = RuntimeException("manualInstall() first called here")
}

LeakCanary 的初始化工程能够概括为 2 项内容:

    1. 初始化 LeakCanary 内部剖析引擎;
    1. 在 Android Framework 上注册 5 种 Android 走漏场景的监控。

2.2 注册 5 种 Android 走漏场景的监控

在初始过程中,对应 5 种场景的内存走漏监控

AppWatcher.kt

fun appDefaultWatchers(
 application: Application,
 reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
 return listOf(
  ActivityWatcher(application, reachabilityWatcher),
  FragmentAndViewModelWatcher(application, reachabilityWatcher),
  RootViewWatcher(reachabilityWatcher),
  ServiceWatcher(reachabilityWatcher)
 )
}

Activity 收回监控

ActivityWatcher 类中 经过Application#registerActivityLifecycleCallbacks(…)接口监听 Activity#onDestroy 事情;

ActivityWatcher.kt

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

Fragment 与 Fragment View 收回监控:

FragmentAndViewModelWatcher 类中经过Application#registerActivityLifecycleCallbacks(…)接口监听 Fragment 的生命周期:

FragmentAndViewModelWatcher.kt

override fun install() {
  application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}

再来看 FragmentAndViewModelWatcher 生命回调的处理:
FragmentAndViewModelWatcher.kt

private val lifecycleCallbacks =
  object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
    override fun onActivityCreated(
      activity: Activity,
      savedInstanceState: Bundle?
    ) {
      for (watcher in fragmentDestroyWatchers) {
        //实际调用的是对应的 invoke 办法
        watcher(activity)
      }
    }
  }

FragmentAndViewModelWatcher.kt

private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
  // fragmentDestroyWatchers 列表,支撑不同 Fragment 实例的检测;
  // 这儿的 watcher 都承继自(Activity)->Unit 表示办法类型/函数类型,
  // 参数为 Activity,回来值为空;由于是办法类型所以需求重写 invoke 办法
  val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
  //Android O 后构建 AndroidOFragmentDestroyWatcher
  if (SDK_INT >= O) {
    fragmentDestroyWatchers.add(
      AndroidOFragmentDestroyWatcher(reachabilityWatcher)
    )
  }
  // 假如 Class.for(className)能找到 androidx.fragment.app.Fragment 和
  // leakcanary.internal.AndroidXFragmentDestroyWatcher 则增加 AndroidXFragmentDestroyWatcher 则增加
  getWatcherIfAvailable(
    ANDROIDX_FRAGMENT_CLASS_NAME,
    ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  }
  //假如 Class.for(className)能找到 android.support.v4.app.Fragment 和
  //leakcanary.internal.AndroidSupportFragmentDestroyWatcher 则增加 AndroidSupportFragmentDestroyWatcher
  getWatcherIfAvailable(
    ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
    ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  }
  fragmentDestroyWatchers
}

以 AndroidX Fragment 为例,AndroidXFragmentDestroyWatcher 的 invoke 办法完结:
AndroidXFragmentDestroyWatcher.kt

override fun invoke(activity: Activity) {
  if (activity is FragmentActivity) {
   //取得对应的 FragmentManager,注册生命周期回调
   val supportFragmentManager = activity.supportFragmentManager
   supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
   //增加了 ViewModelStoreOwner 为 Activity 的 ViewModelClearedWatcher 监测
   ViewModelClearedWatcher.install(activity, reachabilityWatcher)
  }
 }

LeakCanary 在 onFragmentDestroyed 回调里边来处理查看 Fragment 是否正常被收回的检测逻辑。

AndroidXFragmentDestroyWatcher.kt

override fun onFragmentDestroyed(
  fm: FragmentManager,
  fragment: Fragment
) {
  reachabilityWatcher.expectWeaklyReachable(
    fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
  )
}

LeakCanary 在 onFragmentViewDestroyed 回调里边来处理查看 Fragment 的 View 是否正常被收回的检测逻辑。

AndroidXFragmentDestroyWatcher.kt

override fun onFragmentViewDestroyed(
  fm: FragmentManager,
  fragment: Fragment
) {
  val view = fragment.view
  if (view != null) {
    reachabilityWatcher.expectWeaklyReachable(
      view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
      "(references to its views should be cleared to prevent leaks)"
    )
  }

ViewModel 监控

由于 Android Framework 未供给设置 ViewModel#onClear() 大局监听的办法,所以 LeakCanary 是经过 Hook 的办法完结。即:在 Activity#onCreate 和 Fragment#onCreate 事情中实例化一个自定义 ViewModel,在进入 ViewModel#onClear() 办法时,经过反射获取当时效果域中一切的 ViewModel 目标交给 ObjectWatcher 监控。

ViewModelClearedWatcher.kt

// ViewModel 的子类
internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {
  // 反射获取 ViewModelStore 中的 ViewModel 映射表,即可获取当时效果域一切 ViewModel 目标
  private val viewModelMap: Map<String, ViewModel>? = try {
    val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
    mMapField.isAccessible = true
    mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
  } catch (ignored: Exception) {
    null
  }
  override fun onCleared() {
    // 遍历当时效果域一切 ViewModel 目标
    viewModelMap?.values?.forEach { viewModel ->
      // reachabilityWatcher 即 ObjectWatcher
      reachabilityWatcher.expectWeaklyReachable(viewModel /*被监控目标*/, "${viewModel::class.java.name} received ViewModel#onCleared() callback")
    }
  }
  companion object {
    // 直接在 storeOwner 效果域实例化 ViewModelClearedWatcher 目标
    fun install(storeOwner: ViewModelStoreOwner, reachabilityWatcher: ReachabilityWatcher) {
      val provider = ViewModelProvider(storeOwner, object : Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T =
          ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
      })
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
}

RootView 监控

由于 Android Framework 未供给设置大局监听 RootView 从 WindowManager 中移除的办法,所以 LeakCanary 是经过 Hook 的办法完结的,这一块是经过 squareup 另一个开源库curtains完结的。RootView 监控这部分源码也比较复杂了,需求经过 2 步 Hook 来完结:

    1. Hook WMS 服务内部的WindowManagerGlobal.mViewsRootView 列表,获取 RootView 新增和移除的机遇;
    1. 查看 View 对应的 Window 类型,假如是 Dialog 或 DreamService 等类型,则在注册View#addOnAttachStateChangeListener()监听,在其间的 onViewDetachedFromWindow() 回调中将 View 目标交给 ObjectWatcher 监控。

LeakCanary 源码摘要如下:

RootViewWatcher.kt

override fun install() {
  // 1. 注册 RootView 监听
  Curtains.onRootViewsChangedListeners += listener
}
private val listener = OnRootViewAddedListener { rootView ->
  val trackDetached = when(rootView.windowType) {
  PHONE_WINDOW -> {
    when (rootView.phoneWindow?.callback?.wrappedCallback) {
      // Activity 类型现已在 ActivityWatcher 中监控了,不需求重复监控
      is Activity -> false
      is Dialog -> {
        // leak_canary_watcher_watch_dismissed_dialogs:Dialog 监控开关
        val resources = rootView.context.applicationContext.resources
        resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
      }
      // DreamService 屏保等
      else -> true
    }
  }
  POPUP_WINDOW -> false
  TOOLTIP, TOAST, UNKNOWN -> true
  }
  if (trackDetached) {
    // 2. 注册 View#addOnAttachStateChangeListener 监听
    rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
      val watchDetachedView = Runnable {
        // 3. 交给 ObjectWatcher 监控
        reachabilityWatcher.expectWeaklyReachable(rootView /*被监控目标*/ , "${rootView::class.java.name} received View#onDetachedFromWindow() callback")
      }
      override fun onViewAttachedToWindow(v: View) {
        mainHandler.removeCallbacks(watchDetachedView)
      }
      override fun onViewDetachedFromWindow(v: View) {
        mainHandler.post(watchDetachedView)
      }
    })
  }
}

curtains 源码摘要如下:

RootViewsSpy.kt

private val delegatingViewList = object : ArrayList<View>() {
  // 重写 ArrayList#add 办法
  override fun add(element: View): Boolean {
    // 回调
    listeners.forEach { it.onRootViewsChanged(element, true) }
    return super.add(element)
  }
  // 重写 ArrayList#removeAt 办法
  override fun removeAt(index: Int): View {
    // 回调
    val removedView = super.removeAt(index)
    listeners.forEach { it.onRootViewsChanged(removedView, false) }
    return removedView
  }
}
companion object {
  fun install(): RootViewsSpy {
    return RootViewsSpy().apply {
      WindowManagerSpy.swapWindowManagerGlobalMViews { mViews /*原目标*/ ->
        // 新目标(lambda 表达式的末行便是回来值)
        delegatingViewList.apply { addAll(mViews) }
      }
    }
  }
}

WindowManageSpy.kt

// Hook WMS 服务内部的 WindowManagerGlobal.mViews RootView 列表
// swap 是一个 lambda 表达式,参数为原目标,回来值为注入的新目标
fun swapWindowManagerGlobalMViews(swap: (ArrayList<View>) -> ArrayList<View>) {
  windowManagerInstance?.let { windowManagerInstance ->
    mViewsField?.let { mViewsField ->
      val mViews = mViewsField[windowManagerInstance] as ArrayList<View>
      mViewsField[windowManagerInstance] = swap(mViews)
    }
  }
}

Service 收回监听

由于 Android Framework 未供给设置 Service#onDestroy() 大局监听的办法,所以 LeakCanary 是经过 Hook 的办法完结的。

Service 监控这部分源码比较复杂,需求经过 2 步 Hook 来完结:

  • 1、Hook 主线程音讯循环的mH.mCallback回调,监听其间的 STOP_SERVICE 音讯,将行将 Destroy 的 Service 目标暂存起来(由于 ActivityThread.H 中没有 DESTROY_SERVICE 音讯,所以不能直接监听到 onDestroy() 事情,需求第 2 步);
  • 2、运用动态代理 Hook AMS 与 App 通讯的的IActivityManagerBinder 目标,代理其间的serviceDoneExecuting()办法,视为 Service#onDestroy() 的履行机遇,拿到暂存的 Service 目标交给 ObjectWatcher 监控。
    ServiceWatcher.kt
private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
// 暂存行将 Destroy 的 Service
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
override fun install() {
  // 1. Hook mH.mCallback
  swapActivityThreadHandlerCallback { mCallback /*原目标*/ ->
    // uninstallActivityThreadHandlerCallback:用于撤销 Hook
    uninstallActivityThreadHandlerCallback = {
      swapActivityThreadHandlerCallback {
        mCallback
      }
    }
    // 新目标(lambda 表达式的末行便是回来值)
    Handler.Callback { msg ->
      // 1.1 Service#onStop() 事情
      if (msg.what == STOP_SERVICE) {
        val key = msg.obj as IBinder
        // 1.2 activityThreadServices:反射获取 ActivityThread mServices 映射表 <IBinder, CreateServiceData>
        activityThreadServices[key]?.let {
          // 1.3 暂存行将 Destroy 的 Service
          servicesToBeDestroyed[token] = WeakReference(service)
        }
      }
      // 1.4 继续履行 Framework 原有逻辑
      mCallback?.handleMessage(msg) ?: false
    }
  }
  // 2. Hook AMS IActivityManager
  swapActivityManager { activityManagerInterface, activityManagerInstance /*原目标*/ ->
    // uninstallActivityManager:用于撤销 Hook
    uninstallActivityManager = {
      swapActivityManager { _, _ ->
        activityManagerInstance
      }
    }
    // 新目标(lambda 表达式的末行便是回来值)
    Proxy.newProxyInstance(activityManagerInterface.classLoader, arrayOf(activityManagerInterface)) { _, method, args ->
      // 2.1 代理 serviceDoneExecuting() 办法
      if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
        // 2.2 取出暂存的行将 Destroy 的 Service
        val token = args!![0] as IBinder
        if (servicesToBeDestroyed.containsKey(token)) {
          servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
            // 2.3 交给 ObjectWatcher 监控
            serviceWeakReference.get()?.let { service ->
              reachabilityWatcher.expectWeaklyReachable(service /*被监控目标*/, "${service::class.java.name} received Service#onDestroy() callback")
            }
          }
        }
      }
      // 2.4 继续履行 Framework 原有逻辑
      method.invoke(activityManagerInstance, *args)
    }
  }
}
override fun uninstall() {
  // 封闭 mH.mCallback 的 Hook
  uninstallActivityManager?.invoke()
  uninstallActivityThreadHandlerCallback?.invoke()
  uninstallActivityManager = null
  uninstallActivityThreadHandlerCallback = null
}
// 运用反射修改 ActivityThread 的主线程音讯循环的 mH.mCallback
// swap 是一个 lambda 表达式,参数为原目标,回来值为注入的新目标
private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
  val mHField = activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
  val mH = mHField[activityThreadInstance] as Handler
  val mCallbackField = Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
  val mCallback = mCallbackField[mH] as Handler.Callback?
  // 将 swap 的回来值作为新目标,完结 Hook
  mCallbackField[mH] = swap(mCallback)
}
// 运用反射修改 AMS 与 App 通讯的 IActivityManager Binder 目标
// swap 是一个 lambda 表达式,参数为 IActivityManager 的 Class 目标和接口原完结目标,回来值为注入的新目标
private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
  val singletonClass = Class.forName("android.util.Singleton")
  val mInstanceField = singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }
  val singletonGetMethod = singletonClass.getDeclaredMethod("get")
  val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    "android.app.ActivityManager" to "IActivityManagerSingleton"
  } else {
    "android.app.ActivityManagerNative" to "gDefault"
  }
  val activityManagerClass = Class.forName(className)
  val activityManagerSingletonField = activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
  val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]
  // Calling get() instead of reading from the field directly to ensure the singleton is
  // created.
  val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)
  val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
  // 将 swap 的回来值作为新目标,完结 Hook
  mInstanceField[activityManagerSingletonInstance] = swap(iActivityManagerInterface, activityManagerInstance!!)
}

至此,LeakCanary 初始化完结,而且成功在 Android Framework 的各个位置安插监控,完结对 Activity 和 Service 等目标进入无用状况的监听。

2.3 监控内存走漏

完结以上步骤后,会交给ObjectWatcher监控,它主要经过以下 3 步来判别目标是否走漏:

  • 1. 为被监控目标 watchedObject 创立一个 KeyedWeakReference 弱引证,并存储到 <UUID, KeyedWeakReference> 的映射表中;
  • 2. postDelay 五秒后查看引证目标是否出现在引证行列中,出现在行列则阐明被监控目标未产生走漏。随后,移除映射表中未走漏的记载,更新走漏的引证目标的 retainedUptimeMillis 字段以符号为走漏;
  • 3. 经过回调 onObjectRetained 告知 LeakCanary 内部产生新的内存走漏。

AppWatcher.kt

val objectWatcher = ObjectWatcher(
  // lambda 表达式获取当时体系时刻
  clock = { SystemClock.uptimeMillis() },
  // lambda 表达式完结 Executor SAM 接口
  checkRetainedExecutor = {
    mainHandler.postDelayed(it, retainedDelayMillis)
  },
  // lambda 表达式获取监控开关
  isEnabled = { true }
)

ObjectWatcher.kt

class ObjectWatcher constructor(
  private val clock: Clock,
  private val checkRetainedExecutor: Executor,
  private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
  if (!isEnabled()) {
    // 监控开关
    return
  }
  // 被监控的目标映射表 <UUID,KeyedWeakReference>
  private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
  // KeyedWeakReference 相关的引证行列,用于判别目标是否走漏
  private val queue = ReferenceQueue<Any>()
  // 1. 为 watchedObject 目标增加监控
  @Synchronized
  override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    // 1.1 移除 watchedObjects 中未走漏的引证目标
    removeWeaklyReachableObjects()
    // 1.2 新建一个 KeyedWeakReference 引证目标
    val key = UUID.randomUUID().toString()
    val watchUptimeMillis = clock.uptimeMillis()
    watchedObjects[key] = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    // 2. 五秒后查看引证目标是否出现在引证行列中,不然断定产生走漏
    // checkRetainedExecutor 相当于 postDelay 五秒后履行 moveToRetained() 办法
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }
  // 2. 五秒后查看引证目标是否出现在引证行列中,不然阐明产生走漏
  @Synchronized
  private fun moveToRetained(key: String) {
    // 2.1 移除 watchedObjects 中未走漏的引证目标
    removeWeaklyReachableObjects()
    // 2.2 依然存在的引证目标被断定产生走漏
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      // 3. 回调告诉 LeakCanary 内部处理
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }
  // 移除未走漏目标对应的 KeyedWeakReference
  private fun removeWeaklyReachableObjects() {
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        // KeyedWeakReference 出现在引证行列中,阐明未产生走漏
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }
  // 4. Heap Dump 后移除一切监控时刻早于 heapDumpUptimeMillis 的引证目标
  @Synchronized
  fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
    val weakRefsToRemove = watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }
    weakRefsToRemove.values.forEach { it.clear() }
    watchedObjects.keys.removeAll(weakRefsToRemove.keys)
  }
  // 获取是否有内存走漏目标
  val hasRetainedObjects: Boolean
  @Synchronized get() {
    // 移除 watchedObjects 中未走漏的引证目标
    removeWeaklyReachableObjects()
    return watchedObjects.any { it.value.retainedUptimeMillis != -1L }
  }
  // 获取内存走漏目标计数
  val retainedObjectCount: Int
  @Synchronized get() {
    // 移除 watchedObjects 中未走漏的引证目标
    removeWeaklyReachableObjects()
    return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
  }
}

被监控目标watchedObject相关的弱引证目标:

KeyedWeakReference.kt

class KeyedWeakReference(
  // 被监控目标
  referent: Any,
  // 仅有 Key,依据此字段匹配映射表中的记载
  val key: String,
  // 描述信息
  val description: String,
  // 监控开端时刻,即引证目标创立时刻
  val watchUptimeMillis: Long,
  // 相关的引证行列
  referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(referent, referenceQueue) {

  // 记载实际目标 referent 被断定为走漏目标的时刻
  // -1L 表示非走漏目标,或许还未断定完结
  @Volatile
  var retainedUptimeMillis = -1L
  override fun clear() {
    super.clear()
    retainedUptimeMillis = -1L
  }
  companion object {
    // 记载最近一次触发 Heap Dump 的时刻
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L
  }
}

2.4 Dump heap 获取内存快照文件

ObjectWatcher 断定被监控目标产生走漏后,会经过接口办法 OnObjectRetainedListener#onObjectRetained() 回调到 LeakCanary 内部的管理器 InternalLeakCanary 处理(在前文 AppWatcher 初始化中说到过)。LeakCanary 不会每次发现内存走漏目标都进行剖析作业,而会进行两个阻拦:

  • 1. 走漏目标计数未到达阈值,或许进入后台时刻未到达阈值;
  • 2. 核算间隔上一次 HeapDump 未超越 60s。
    源码摘要如下:

InternalLeakCanary.kt

// 从 ObjectWatcher 回调过来
override fun onObjectRetained() = scheduleRetainedObjectCheck()
private lateinit var heapDumpTrigger: HeapDumpTrigger
fun scheduleRetainedObjectCheck() {
  if (this::heapDumpTrigger.isInitialized) {
    heapDumpTrigger.scheduleRetainedObjectCheck()
  }
}

HeapDumpTrigger.kt

fun scheduleRetainedObjectCheck(delayMillis: Long = 0L) {
  // 已简化:源码此处运用时刻戳阻拦,避免重复 postDelayed
  backgroundHandler.postDelayed({
    checkRetainedObjects()
  }, delayMillis)
}
private fun checkRetainedObjects() {
  val config = configProvider()
  // 走漏目标计数
  var retainedReferenceCount = objectWatcher.retainedObjectCount
  if (retainedReferenceCount > 0) {
    // 自动触发 GC,并等候 100 ms
    gcTrigger.runGc()
    // 重新获取走漏目标计数
    retainedReferenceCount = objectWatcher.retainedObjectCount
  }
  // 阻拦 1:走漏目标计数未到达阈值,或许进入后台时刻未到达阈值
  if (retainedKeysCount < retainedVisibleThreshold) {
    // App 坐落前台或许刚刚进入后台
    if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
      // 发送告诉提示
      showRetainedCountNotification("App visible, waiting until %d retained objects")
      // 推迟 2 秒再查看
      scheduleRetainedObjectCheck(WAIT_FOR_OBJECT_THRESHOLD_MILLIS)
      return;
    }
  }
  // 阻拦 2:核算间隔上一次 HeapDump 未超越 60s
  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
    // 发送告诉提示
    showRetainedCountNotification("Last heap dump was less than a minute ago")
    // 推迟 (60 - elapsedSinceLastDumpMillis)s 再查看
    scheduleRetainedObjectCheck(WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis)
    return
  }
  // 移除告诉提示
  dismissRetainedCountNotification()
  // 触发 HeapDump(此刻,运用有可能在后台)
  dumpHeap(...)
}
// 真实开端履行 Heap Dump
private fun dumpHeap(...) {
  // 1. 获取文件存储供给器
  val directoryProvider = InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
  // 2. 创立 .hprof File 文件
  val heapDumpFile = directoryProvider.newHeapDumpFile()
  // 3. 履行 Heap Dump
  // Heap Dump 开端时刻戳
  val heapDumpUptimeMillis = SystemClock.uptimeMillis()
  // heapDumper.dumpHeap:终究调用 Debug.dumpHprofData(heapDumpFile.absolutePath)
  configProvider().heapDumper.dumpHeap(heapDumpFile)
  // 4. 清除 ObjectWatcher 中过期的监控
  objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
  // 5. 剖析堆快照
  InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))
}

2.5 剖析堆快照

在前面的作业中,LeakCanary 现已成功生成 .hprof 堆快照文件,而且发送了一个 LeakCanary 内部事情 HeapDump。LeakCanary 的装备项中设置了多个事情顾客 EventListener,其间与 HeapDump 事情有关的是 when{} 代码块中三个顾客。不过,这三个顾客并不是并存的,而是会依据 App 当时的依靠项而挑选最优的履行战略:

  • 1 – WorkerManager 多进程剖析
  • 2 – WorkManager 异步剖析
  • 3 – 异步线程剖析(兜底战略)
    LeakCanary 装备项中的事情顾客:

LeakCanary.kt

data class Config(
  val eventListeners: List<EventListener> = listOf(
    LogcatEventListener,
    ToastEventListener,
    LazyForwardingEventListener {
      if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
    },
    when {
      // 战略 1 - WorkerManager 多进程剖析
      RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->RemoteWorkManagerHeapAnalyzer
      // 战略 2 - WorkManager 异步剖析
      WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
      // 战略 3 - 异步线程剖析(兜底战略)
      else -> BackgroundThreadHeapAnalyzer
    }
  ),
  ...
)

战略 1 – WorkerManager 多进程剖析: 判别是否能够类加载 RemoteLeakCanaryWorkerService,这个类坐落前文说到的 com.squareup.leakcanary:leakcanary-android-process:2.9.1 依靠中。假如能够类加载成功则视为有依靠,运用 WorkerManager 多进程剖析;

RemoteWorkManagerHeapAnalyzer.kt

object RemoteWorkManagerHeapAnalyzer : EventListener {
  // 经过类加载是否成功,判别是否存在依靠
  internal val remoteLeakCanaryServiceInClasspath by lazy {
    try {
      Class.forName("leakcanary.internal.RemoteLeakCanaryWorkerService")
      true
    } catch (ignored: Throwable) {
      false
    }
  }
  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      // 创立并分发 WorkManager 多进程恳求
      val heapAnalysisRequest = OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class.java).apply {
        val dataBuilder = Data.Builder()
          .putString(ARGUMENT_PACKAGE_NAME, application.packageName)
          .putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)
        setInputData(event.asWorkerInputData(dataBuilder))
        with(WorkManagerHeapAnalyzer) {
          addExpeditedFlag()
        }
      }.build()
      WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
    }
  }
}

RemoteHeapAnalyzerWorker.kt

internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) : RemoteListenableWorker(appContext, workerParams) {
  override fun startRemoteWork(): ListenableFuture<Result> {
    val heapDump = inputData.asEvent<HeapDump>()
    val result = SettableFuture.create<Result>()
    heapAnalyzerThreadHandler.post {
      // 1.1 剖析堆快照
      val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {
        result.isCancelled
      }) { progressEvent ->
        // 1.2 发送剖析进展事情
        if (!result.isCancelled) {
          InternalLeakCanary.sendEvent(progressEvent)
        }
      }
      // 1.3 发送剖析完结事情
      InternalLeakCanary.sendEvent(doneEvent)
      result.set(Result.success())
    }
    return result
  }
}
  • 战略 2 – WorkManager 异步剖析:判别是否能够类加载androidx.work.WorkManager,假如能够,则运用 WorkManager 异步剖析;

WorkManagerHeapAnalyzer.kt

internal val validWorkManagerInClasspath by lazy {
  // 判别 WorkManager 依靠,代码略
}
override fun onEvent(event: Event) {
  if (event is HeapDump) {
    // 创立并分发 WorkManager 恳求
    val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
      setInputData(event.asWorkerInputData())
      addExpeditedFlag()
    }.build()
    val application = InternalLeakCanary.application
    WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
  }
}

HeapAnalyzerWorker.kt

internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
  override fun doWork(): Result {
    // 2.1 剖析堆快照
    val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(inputData.asEvent()) { event ->
      // 2.2 发送剖析进展事情
      InternalLeakCanary.sendEvent(event)
    }
    // 2.3 发送剖析完结事情
    InternalLeakCanary.sendEvent(doneEvent)
    return Result.success()
  }
}
  • 战略 3 – 异步线程剖析(兜底战略):假如以上战略未射中,则直接运用子线程兜底履行。

BackgroundThreadHeapAnalyzer.kt

object BackgroundThreadHeapAnalyzer : EventListener {
  // HandlerThread
  internal val heapAnalyzerThreadHandler by lazy {
    val handlerThread = HandlerThread("HeapAnalyzer")
    handlerThread.start()
    Handler(handlerThread.looper)
  }
  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      // HandlerThread 恳求
      heapAnalyzerThreadHandler.post {
        // 3.1 剖析堆快照
        val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
          // 3.2 发送剖析进展事情
          InternalLeakCanary.sendEvent(event)
        }
        // 3.3 发送剖析完结事情
        InternalLeakCanary.sendEvent(doneEvent)
      }
    }
  }
}

能够看到,不论选用那种履行战略,终究履行的逻辑都是相同的:

    1. 剖析堆快照;
    1. 发送剖析进展事情;
    1. 发送剖析完结事情。

在前面的剖析中,咱们现已知道 LeakCanary 是经过子线程或许子进程履行 AndroidDebugHeapAnalyzer.runAnalysisBlocking 办法来剖析堆快照的,并在剖析过程中和剖析完结后发送回调事情。现在咱们来阅读 LeakCanary 的堆快照剖析过程:

AndroidDebugHeapAnalyzer.kt

fun runAnalysisBlocking(
  heapDumped: HeapDump,
  isCanceled: () -> Boolean = { false },
  progressEventListener: (HeapAnalysisProgress) -> Unit
): HeapAnalysisDone<*> {
  ...
  // 1. .hprof 文件
  val heapDumpFile = heapDumped.file
  // 2. 剖析堆快照
  val heapAnalysis = analyzeHeap(heapDumpFile, progressListener, isCanceled)
  val analysisDoneEvent = ScopedLeaksDb.writableDatabase(application) { db ->
  // 3. 将剖析陈述耐久化到 DB
  val id = HeapAnalysisTable.insert(db, heapAnalysis)
  // 4. 发送剖析完结事情(回来到上一级进行发送:InternalLeakCanary.sendEvent(doneEvent))
  val showIntent = LeakActivity.createSuccessIntent(application, id)
  val leakSignatures = fullHeapAnalysis.allLeaks.map { it.signature }.toSet()
  val leakSignatureStatuses = LeakTable.retrieveLeakReadStatuses(db, leakSignatures)
  val unreadLeakSignatures = leakSignatureStatuses.filter { (_, read) -> !read}.keys.toSet()
    HeapAnalysisSucceeded(heapDumped.uniqueId, fullHeapAnalysis, unreadLeakSignatures ,showIntent)
  }
  return analysisDoneEvent
}

开端进入 Shark 组件:

shark.HeapAnalyzer.kt

// analyze -> analyze -> FindLeakInput.analyzeGraph
private fun FindLeakInput.analyzeGraph(
  metadataExtractor: MetadataExtractor,
  leakingObjectFinder: LeakingObjectFinder,
  heapDumpFile: File,
  analysisStartNanoTime: Long
): HeapAnalysisSuccess {
  ...
  // 1. 在堆快照中寻觅走漏目标,默认是寻觅 KeyedWeakReference 类型目标
  // leakingObjectFinder 默认是 KeyedWeakReferenceFinder
  val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)
  // 2. 剖析走漏目标的最短引证链,并依照运用链签名分类
  // applicationLeaks: Application Leaks
  // librbuildLeakTracesaryLeaks:Library Leaks
  // unreachableObjects:LeakCanary 无法剖分出强引证链,能够提 Stack Overflow
  val (applicationLeaks, libraryLeaks, unreachableObjects) = findLeaks(leakingObjectIds)
  // 3. 回来剖析完结事情
  return HeapAnalysisSuccess(...)
}
private fun FindLeakInput.findLeaks(leakingObjectIds: Set<Long>): LeaksAndUnreachableObjects {
  // PathFinder:引证链剖析器
  val pathFinder = PathFinder(graph, listener, referenceReader, referenceMatchers)
  // pathFindingResults:完整引证链
  val pathFindingResults = pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)
  // unreachableObjects:LeakCanary 无法剖分出强引证链(相当于 LeakCanary 的 Bug)
  val unreachableObjects = findUnreachableObjects(pathFindingResults, leakingObjectIds)
  // shortestPaths:最短引证链
  val shortestPaths = deduplicateShortestPaths(pathFindingResults.pathsToLeakingObjects)
  // inspectedObjectsByPath:符号信息
  val inspectedObjectsByPath = inspectObjects(shortestPaths)
  // retainedSizes:走漏内存大小
  val retainedSizes = computeRetainedSizes(inspectedObjectsByPath, pathFindingResults.dominatorTree)
  // 生成单个走漏问题的剖析陈述,并依照运用链签名分组,依照 Application Leaks 和 Library Leaks 分类,依照 Application Leaks 和 Library Leaks 分类
  // applicationLeaks: Application Leaks
  // librbuildLeakTracesaryLeaks:Library Leaks
  val (applicationLeaks, librbuildLeakTracesaryLeaks) = buildLeakTraces(shortestPaths, inspectedObjectsByPath, retainedSizes)
  return LeaksAndUnreachableObjects(applicationLeaks, libraryLeaks, unreachableObjects)
}

能够看到,堆快照剖析终究是交给 Shark 中的 HeapAnalizer 完结的,中心流程是:

  • 1、在堆快照中寻觅走漏目标,默认是寻觅 KeyedWeakReference 类型目标;
  • 2、剖析 KeyedWeakReference 目标的最短引证链,并依照引证链签名分组,依照 Application Leaks 和 Library Leaks 分类;
  • 3、回来剖析完结事情。

侧重看最复杂的第 2 步:

shark.HeapAnalyzer.kt

// 生成单个走漏问题的剖析陈述,并依照运用链签名分组,依照 Application Leaks 和 Library Leaks 分类,依照 Application Leaks 和 Library Leaks 分类
private fun FindLeakInput.buildLeakTraces(
  shortestPaths: List<ShortestPath> /*最短引证链*/ ,
  inspectedObjectsByPath: List<List<InspectedObject>> /*符号信息*/ ,
  retainedSizes: Map<Long, Pair<Int, Int>>? /*走漏内存大小*/
): Pair<List<ApplicationLeak>, List<LibraryLeak>> {
  // Application Leaks
  val applicationLeaksMap = mutableMapOf<String, MutableList<LeakTrace>>()
  // Library Leaks
  val libraryLeaksMap = mutableMapOf<String, Pair<LibraryLeakReferenceMatcher, MutableList<LeakTrace>>>()
  shortestPaths.forEachIndexed { pathIndex, shortestPath ->
    // 符号信息
    val inspectedObjects = inspectedObjectsByPath[pathIndex]
    // 实例化引证链上的每个目标快照(非置疑目标的 leakingStatus 为 NOT_LEAKING)
    val leakTraceObjects = buildLeakTraceObjects(inspectedObjects, retainedSizes)
    val referencePath = buildReferencePath(shortestPath, leakTraceObjects)
    // 剖析陈述
    val leakTrace = LeakTrace(
      gcRootType = GcRootType.fromGcRoot(shortestPath.root.gcRoot),
      referencePath = referencePath,
      leakingObject = leakTraceObjects.last()
    )
    val firstLibraryLeakMatcher = shortestPath.firstLibraryLeakMatcher()
    if (firstLibraryLeakMatcher != null) {
      // Library Leaks
      val signature: String = firstLibraryLeakMatcher.pattern.toString().createSHA1Hash()
      libraryLeaksMap.getOrPut(signature) { firstLibraryLeakMatcher to mutableListOf() }.second += leakTrace
    } else {
      // Application Leaks
      applicationLeaksMap.getOrPut(leakTrace.signature) { mutableListOf() } += leakTrace
    }
  }
  val applicationLeaks = applicationLeaksMap.map { (_, leakTraces) ->
    // 实例化为 ApplicationLeak 类型
    ApplicationLeak(leakTraces)
  }
  val libraryLeaks = libraryLeaksMap.map { (_, pair) ->
    // 实例化为 LibraryLeak 类型
    val (matcher, leakTraces) = pair
    LibraryLeak(leakTraces, matcher.pattern, matcher.description)
  }
  return applicationLeaks to libraryLeaks
}
// 生成单个走漏问题的剖析陈述,并依照运用链签名分组,依照 Application Leaks 和 Library Leaks 分类,依照 Application Leaks 和 Library Leaks 分类
private fun FindLeakInput.buildLeakTraces(
  shortestPaths: List<ShortestPath> /*最短引证链*/ ,
  inspectedObjectsByPath: List<List<InspectedObject>> /*符号信息*/ ,
  retainedSizes: Map<Long, Pair<Int, Int>>? /*走漏内存大小*/
): Pair<List<ApplicationLeak>, List<LibraryLeak>> {
  // Application Leaks
  val applicationLeaksMap = mutableMapOf<String, MutableList<LeakTrace>>()
  // Library Leaks
  val libraryLeaksMap = mutableMapOf<String, Pair<LibraryLeakReferenceMatcher, MutableList<LeakTrace>>>()
  shortestPaths.forEachIndexed { pathIndex, shortestPath ->
    // 符号信息
    val inspectedObjects = inspectedObjectsByPath[pathIndex]
    // 实例化引证链上的每个目标快照(非置疑目标的 leakingStatus 为 NOT_LEAKING)
    val leakTraceObjects = buildLeakTraceObjects(inspectedObjects, retainedSizes)
    val referencePath = buildReferencePath(shortestPath, leakTraceObjects)
    // 剖析陈述
    val leakTrace = LeakTrace(
      gcRootType = GcRootType.fromGcRoot(shortestPath.root.gcRoot),
      referencePath = referencePath,
      leakingObject = leakTraceObjects.last()
    )
    val firstLibraryLeakMatcher = shortestPath.firstLibraryLeakMatcher()
    if (firstLibraryLeakMatcher != null) {
      // Library Leaks
      val signature: String = firstLibraryLeakMatcher.pattern.toString().createSHA1Hash()
      libraryLeaksMap.getOrPut(signature) { firstLibraryLeakMatcher to mutableListOf() }.second += leakTrace
    } else {
      // Application Leaks
      applicationLeaksMap.getOrPut(leakTrace.signature) { mutableListOf() } += leakTrace
    }
  }
  val applicationLeaks = applicationLeaksMap.map { (_, leakTraces) ->
    // 实例化为 ApplicationLeak 类型
    ApplicationLeak(leakTraces)
  }
  val libraryLeaks = libraryLeaksMap.map { (_, pair) ->
    // 实例化为 LibraryLeak 类型
    val (matcher, leakTraces) = pair
    LibraryLeak(leakTraces, matcher.pattern, matcher.description)
  }
  return applicationLeaks to libraryLeaks
}

2.6 输出剖析陈述

LeakCanary 会运用 ObjectInspector 目标检索器在引证链上的节点中符号必要的信息和状况,符号信息会显示在剖析陈述中,而且会影响陈述中的提示。而引证链 LEAKING 节点以后到第一个 NOT_LEAKING 节点中间的节点,才会用 ~~~ 下划线符号为置疑目标。

LeakCanary 经过 leakingObjectFinder 符号引证信息,leakingObjectFinder 默认是 AndroidObjectInspectors.appDefaults,也能够在装备项中自定义。

// inspectedObjectsByPath:筛选出非置疑目标(剖析陈述中 ~~~ 符号的是置疑目标)
val inspectedObjectsByPath = inspectObjects(shortestPaths)

看一下可视化陈述中相关源码:

DisplayLeakAdapter.kt

...
val reachabilityString = when (leakingStatus) {
  UNKNOWN -> extra("UNKNOWN")
  NOT_LEAKING -> "NO" + extra(" (${leakingStatusReason})")
  LEAKING -> "YES" + extra(" (${leakingStatusReason})")
}
...

LeakTrace.kt

// 是否为置疑目标
fun referencePathElementIsSuspect(index: Int): Boolean {
  return when (referencePath[index].originObject.leakingStatus) {
    UNKNOWN -> true
    NOT_LEAKING -> index == referencePath.lastIndex || referencePath[index + 1].originObject.leakingStatus != NOT_LEAKING
    else -> false
  }
}

有两个位置处理了HeapAnalysisSucceeded事情:

  • Logcat:打印剖析陈述日志;
  • Notification: 发送剖析成功体系告诉音讯。

LogcatEventListener.kt

 object LogcatEventListener : EventListener {
  ...
  SharkLog.d { "\u200B\n${LeakTraceWrapper.wrap(event.heapAnalysis.toString(), 120)}" }
  ...
}

NotificationEventListener.kt

object NotificationEventListener : EventListener {
  ...
  val flags = if (Build.VERSION.SDK_INT >= 23) {
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
  } else {
    PendingIntent.FLAG_UPDATE_CURRENT
  }
  // 点击告诉音讯翻开可视化剖析陈述
  val pendingIntent = PendingIntent.getActivity(appContext, 1, event.showIntent, flags)
  showHeapAnalysisResultNotification(contentTitle,pendingIntent)
  ...
}

2.7 小结

最后来总结下 LeakCanary 内存走漏剖析过程:

    1. 初始化
    1. 注册 5 种 Android 走漏场景的监控
    1. 收到销毁回调后,依据要收回目标创立 KeyedWeakReference 并相关 ReferenceQueue
    1. 推迟 5 秒查看相关目标是否被收回
    1. 假如未被收回则开启服务,dump heap 获取内存快照.hprof文件
    1. 经过 Shark 库解析.hprof 文件,获取走漏目标,核算走漏目标到 GC roots 的最短途径
    1. 合并多个走漏途径并输出剖析结果
    1. 将结果展示到可视化界面

三 参阅文献

为什么各大厂自研的内存走漏检测框架都要参阅 LeakCanary?由于它是真强啊!