探究LeakCanary的原理

最近结合网上的文章看了下LeakCanary的代码,了解原理之后,顿时出现了我上我也行的感觉,对此记录一下。

实现思路

leakcanary主要是监控activity和fragment的内存泄露,这里先说下leakcanary的实现思路,

  1. 监控activity和fragment的生命周期,搭配Reference引用,判断是否被回收;
  2. 在确定内存泄露之后,获取内存快照,分析具体内存泄露的对象引用链;

本文主要是分析第一点,对于获取内存快照和分析就不在本文阐述。

先大致分析下,如何实现activity和fragment对象的内存泄露监控:

  1. 使用Application.registerActivityLifecycleCallbacks(callback)注册activity的生命周期监听,Fragment则使用Activity.FragmentManager.registerFragmentLifecycleCallbacks(callback);
  2. 在onDestroy时,使用Reference关联引用;
  3. 等待一段时间(默认5s),在手动GC后判断对象是否被回收。

源码解析

leakcanary版本为 com.squareup.leakcanary:leakcanary-android:2.3

先找下初始化的入口函数

image.png

在AndroidManifest.xml中,注册了contentProvider

<application>
        <provider
            android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
            android:authorities="${applicationId}.leakcanary-installer"
            android:exported="false" />
    </application>
复制代码

看下leakcanary.internal.AppWatcherInstaller$MainProcess这个类的onCreate()方法,这个方法会在Application的onCreate()之前执行。

internal sealed class AppWatcherInstaller : ContentProvider() {
    //这里MainProcess实现了AppWatcherInstaller,啥都没改,直接看AppWatcherInstaller的onCreate()方法
    internal class MainProcess : AppWatcherInstaller()
    
    override fun onCreate(): Boolean {
        val application = context!!.applicationContext as Application
        InternalAppWatcher.install(application)    //? 1
        return true
    }
}
复制代码

这里就是LeakCanary的初始化入口了,看下标记1处的InternalAppWatcher.install(application)

internal object InternalAppWatcher {

    fun install(application: Application) {
        ...
        //注入Activity destroy的监听
        ActivityDestroyWatcher.install(application, objectWatcher, configProvider)  //? 2
        //注入Fragment destroy的监听
        FragmentDestroyWatcher.install(application, objectWatcher, configProvider)  //? 3
        onAppWatcherInstalled(application)  //? 4
  }
}
复制代码

标记23处,就是前面提到的,注册Activity和Fragment的onDestroy监听,两个类差不多,我就只贴一个

internal class ActivityDestroyWatcher private constructor(
    private val objectWatcher: ObjectWatcher, 
    private val configProvider: () -> Config
) {

  companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
      //调用Application的registerActivityLifecycleCallbacks方法注册Activity的生命周期监听
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)  //? 5
    }
  }
  
  //Activity的onDestroy方法调用后,会回调到此处
  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
          //把处于destroy的Activity传递给ObjectWatcher#watch
          objectWatcher.watch(
              activity, "${activity::class.java.name} received Activity#onDestroy() callback"
          )  //? 6
        }
      }
    }
}
复制代码

标记5处,使用application注入Activity的生命周期监听,注入的监听对象在onActivityDestroyed方法被调用时执行objectWatcher.watch()方法,传入activity,也就是标记6

这个objectWatcher对象是在调用install时传入的参数,需要回到InternalAppWatcher类的install方法看下objectWatcher的初始化

回头(过程省略)在InternalAppWatcher类中发现,objectWatcher是它的成员变量,是ObjectWatcher类的实例

那么接下来就需要看下ObjectWatcher类的watch方法

class ObjectWatcher(...){
  @Synchronized fun watch(
        watchedObject: Any,  description: String
      ) {
          //将已回收的观察对象引用移除掉
        removeWeaklyReachableObjects()  //? 7
        val key = UUID.randomUUID().toString()
        val watchUptimeMillis = clock.uptimeMillis()
         
         //KeyedWeakReference是拓展了WeakReference类,添加了一些变量,如开始监听时间、类的描述、关联的Key等等
          //这里的queue需要注意,它是ReferenceQueue类,与WeakReference关联后,若对象被回收,WeakReference就会被添加到queue中
        val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
          
          //watchedObjects是Map<String, KeyedWeakReference>结构,用来缓存所有的监听对象中对象,
          //如果发现已经被回收(即在queue中出现),则会根据key来移除当前对象的缓存。具体移除逻辑看removeWeaklyReachableObjects()方法
        watchedObjects[key] = reference   
        checkRetainedExecutor.execute {
           
           //这里是做一个延时(默认5秒),实际调用的是Handler.postDelayed(Runnable, delayMillis)
           //延时后观察对象是否被回收,如果没有被回收,就会手动调用gc,gc若还有未被回收的对象,那么可以判定这个对象内存泄露
          moveToRetained(key)    //? 8
        }
      }
    }
复制代码

先看下标记7处的removeWeaklyReachableObjects()是怎么移除已回收的对象引用

 private fun removeWeaklyReachableObjects() {
    var ref: KeyedWeakReference?
    do {
      //遍历queue,如果不为空,那么说明有观察对象被回收
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        //根据key移除缓存中的观察对象引用  
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }
复制代码

不明白WeakReference和ReferenceQueue的可以查看我的另一篇文章[Reference和ReferenceQueue的联动效果]

接着看前面标记8处的moveToRetained(key)方法

  @Synchronized private fun moveToRetained(key: String) {
    //同样,先将已回收的观察对象移除掉
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      //记录观察对象被保留的时间,默认是-1,后面需要根据此处的赋值判断是否属于内存泄露
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()      //? 9
      onObjectRetainedListeners.forEach { it.onObjectRetained() }  //? 10
    }
  }
复制代码

这里补充下标记10处的onObjectRetainedListeners,是通过ObjectWatcher类的addOnObjectRetainedListener()方法添加的

//前面标记1处调用InternalAppWatcher.install(..)时,

internal object InternalAppWatcher {

   private val onAppWatcherInstalled: (Application) -> Unit

   init {
   //反射获取了"leakcanary.internal.InternalLeakCanary"的"INSTANCE"属性
   //这里InternalLeakCanary类是用object修饰的,是个单例类,那么INSTANCE也就是InternalLeakCanary的实例
    val internalLeakCanary = try {
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
          .get(null)
    } catch (ignored: Throwable) {
      NoLeakCanary
    }
    @kotlin.Suppress("UNCHECKED_CAST")
    onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
  }

   fun install(application: Application) {
       ...
       //声明的类型是一个(Application) -> Unit函数,而InternalLeakCanary实现了这个函数,
       //最终调用InternalLeakCanary类的invoke(application: Application)方法
       onAppWatcherInstalled(application)
   }
}
   
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener{
   override fun invoke(application: Application) {
       ...
       //前面标记10处的onObjectRetainedListeners在此处注入
       AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
       ...
   }
}
复制代码

接着看标记10onObjectRetainedListeners.forEach { it.onObjectRetained() }

也就是InternalLeakCanaryonObjectRetained()方法

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener{
  override fun onObjectRetained() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.onObjectRetained()
    }
  }
  
  //在invoke(application)中初始化
  var heapDumpTrigger = HeapDumpTrigger(
        application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
        configProvider
    )
}
复制代码

继续看HeapDumpTriggeronObjectRetained()

internal class HeapDumpTrigger(..){
    
    fun onObjectRetained() {
        scheduleRetainedObjectCheck(
            reason = "found new object retained",
            rescheduling = false
    )
    
    private fun scheduleRetainedObjectCheck(
      reason: String,
      rescheduling: Boolean,
      delayMillis: Long = 0L
    ) {
        ...  
        //子线程中执行检查和gc操作
        backgroundHandler.postDelayed({
          checkRetainedObjects(reason)
        }, delayMillis)
    }
  
    private fun checkRetainedObjects(reason: String) {
        ...
        //未回收的观察对象 且 保留时间不等于-1  (标记9处赋值的保留时间)
        var retainedReferenceCount = objectWatcher.retainedObjectCount

        if (retainedReferenceCount > 0) {
          //手动GC  
          gcTrigger.runGc()
          retainedReferenceCount = objectWatcher.retainedObjectCount
        }
        //如果处于观察期未回收的对象数量 小于 配置可容许的观察数,
        if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
        
        //处于debug的断点状态时,可能会导致线程阻塞,需要暂时延迟执行这种情况导致的未及时回收
        if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
          onRetainInstanceListener.onEvent(DebuggerIsAttached)
          showRetainedCountNotification(
              objectCount = retainedReferenceCount,
              contentText = application.getString(
                  R.string.leak_canary_notification_retained_debugger_attached
              )
          )
          //debug状态下延迟检查
          scheduleRetainedObjectCheck(
              reason = "debugger is attached",
              rescheduling = true,
              delayMillis = WAIT_FOR_DEBUG_MILLIS
          )
          return
        }
        ...
        //确定有对象内存泄露,获取内存快照并分析内存泄露引用链
        dumpHeap(retainedReferenceCount, retry = true)
    }
}
复制代码

看到这里,对象的内存泄露监控逻辑就走完了。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享