LeakCanary的简单介绍及使用

1简介:

LeakCanray是Square开源的Java内存泄漏分析工具,用于在开发阶段检测Android应用中常见中的内存泄漏。

  • 它支持以下五种Android场景中的内存泄漏监测:
    1. 已销毁的 Activity 对象(进入 DESTROYED 状态)
    2. 已销毁的 Fragment 对象和 Fragment View 对象(进入 DESTROYED 状态)
    3. 已清除的的 ViewModel 对象(进入 CLEARED 状态)
    4. 已销毁的的 Service 对象(进入 DESTROYED 状态)
    5. 已从 WindowManager 中移除的 RootView 对象

1.2 使用

  • 只需要在 build.gradle 中添加 LeakCanary 依赖即可(LeakCanary内部默认使用了ContentProvider实现无侵入初始化)。注意:LeakCanary 是只在开发环境使用的工具,所以记得使用 debugImplementation 来配置依赖
// 普通ContentProvider方式
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'

// 可选,使用WorkerManager多进程分析堆快照提升分析速度
debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.12'

2. 源码分析

2.1 初始化

  • 1.x 时期的 LeakCanary 是需要在 Application.onCreate()中加入初始化代码的,2.x之后LeakCanary借助了ContentProvider 的启动机制来间接调用初始化 API, 实现了无侵入的 LeakCanary 初始化。
  • 具体的位置是在 leakcanary-object-watcher-android 这个子module中

 
   
   

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

 真正的初始化代码为AppWatcher.manualInstall(application)

// AppWatcher.kt
/** LeakCanary初始化 */
@JvmOverloads
fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默认5s后进行泄漏检测
  watchersToInstall: List = appDefaultWatchers(application)
) {
  checkMainThread()
  ...
  // 初始化 InternalLeakCanary 内部引擎
  LeakCanaryDelegate.loadLeakCanary(application)
  // 遍历五种监听器进行分别注册
  watchersToInstall.forEach {
    it.install()
  }
  ...
}

/** 创建监听集合 */
fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List {
  return listOf(
    // 对应5中Android泄漏场景(Activity、Fragment和ViewModel、View、Service)
    // 传入的reachabilityWatcher均为ReachabilityWatcher的唯一实现类ObjectWatcher
    ActivityWatcher(application, reachabilityWatcher),
    FragmentAndViewModelWatcher(application, reachabilityWatcher),
    RootViewWatcher(reachabilityWatcher),
    ServiceWatcher(reachabilityWatcher)
  )
}

 分为四个监控类分别对Activity、Fragment和ViewModel、RootView以及Service分别监控,接下来分析下这四个类是如何具体监控的

2.2 四大监控类实现五种泄漏场景的监控

2.2.1 ActivityWatcher

  • ActivityWatcher主要实现对Activity的监控
/** Activity的泄漏监听 */
class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

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

  override fun install() {
    // 注册监听的方式
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

  • Activity的监听比较简单,直接使用提供的全局监听的Api application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)即可,然后在每个Activity的onDestroy中将activity交给objectWatcher去分析即可

2.2.2 FragmentAndViewModelWatcher

  • FragmentAndViewModelWatcher主要实现对Fragment、Fragment view以及ViewModel的监控
/**
 * 主要负责Fragment的泄漏监听(通过Fragment.onDestroy())
 * Fragment View的泄漏监听(通过Fragment.onDestroyView())
 * ViewModel的泄漏监听(通过ViewModel.onCleared())
 */
class FragmentAndViewModelWatcher(
    private val application: Application,
    private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

    /** Fragment监测集合(主要包含几种包下的Fragment) */
    // 集合的类型是(Activity)->Unit的函数类型
    private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
        val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

        // Android O(8.0)后使用AndroidOFragmentDestroyWatcher监听
        if (SDK_INT >= O) {
            fragmentDestroyWatchers.add(
                AndroidOFragmentDestroyWatcher(reachabilityWatcher)
            )
        }

        // androidx包下的Fragment使用AndroidXFragmentDestroyWatcher监听
        getWatcherIfAvailable(
            ANDROIDX_FRAGMENT_CLASS_NAME,
            ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
            reachabilityWatcher
        )?.let {
            fragmentDestroyWatchers.add(it)
        }

        // android.support.v4包下的Fragment使用AndroidSupportFragmentDestroyWatcher监听
        getWatcherIfAvailable(
            ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
            ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
            reachabilityWatcher
        )?.let {
            fragmentDestroyWatchers.add(it)
        }
        fragmentDestroyWatchers
    }

    private val lifecycleCallbacks =
        object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
            override fun onActivityCreated(
                activity: Activity,
                savedInstanceState: Bundle?
            ) {
                // 监听每个Activity的onActivityCreated(),再在其中进行Fragment监听
                for (watcher in fragmentDestroyWatchers) {
                    // 由于fragmentDestroyWatchers里面本身存储的是一个(Activity)->Unit的函数类型
                    // 所以这里可以直接使用watcher(activity),直接调用,参数为activity,
                    // 实际执行的是watcher中的invoke()
                    watcher(activity)
                }
            }
        }

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

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

  • Fragment 与 Fragment View 监控 主要是通过 FragmentAndViewModelWatcher 实现,首先是通过 Application.registerActivityLifecycleCallbacks(…) 接口监听 Activity.onCreate()事件,再通过 FragmentManager.registerFragmentLifecycleCallbacks(…) 接口监听 Fragment 的生命周期
  • 这里根据Android版本以及Fragment包不同分了三种状态,分别是AndroidOFragmentDestroyWatcherAndroidXFragmentDestroyWatcherAndroidSupportFragmentDestroyWatcher,这里以AndroidXFragmentDestroyWatcher为例
/** Androidx包下的Fragment、FragmentView以及ViewModel监听 */
internal class AndroidXFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {

  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      // 注册Fragment级别的ViewModel Hook
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null) {
        // 监听FragmentView.onDestroy()将Fragment.View交给ObjectWatcher分析
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      // 监听Fragment.onDestroy()将Fragment交给ObjectWatcher分析
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

  override fun invoke(activity: Activity) {
    // 这段代码会在Activity.onCreate()中执行
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      // 注册Fragment生命周期监听
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      // 注册Activity级别的ViewModel Hook
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
}

  • 这里面还包含了ViewModel的监听逻辑,分别在 Activity.onCreate() 和 Fragment.onCreate()中使用 ViewModelClearedWatcher进行监听,由于系统并未提供ViewModel全局监听的方法,所以 ViewModelClearedWatcher 是通过Hook方式实现的,我们看下ViewModelClearedWatcher
internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {

  // 直接通过反射获取ViewModelStore类中的map变量(后面改成mMap),即可获得作用域中的所有ViewModel对象
  private val viewModelMap: Map? = try {
    val storeClass = ViewModelStore::class.java
    val mapField = try {
      storeClass.getDeclaredField("map")
    } catch (exception: NoSuchFieldException) {
      storeClass.getDeclaredField("mMap")
    }
    mapField.isAccessible = true
    @Suppress("UNCHECKED_CAST")
    mapField[storeOwner.viewModelStore] as Map
  } catch (ignored: Exception) {
    SharkLog.d(ignored) { "Could not find ViewModelStore map of view models" }
    null
  }

  override fun onCleared() {
    // 遍历当前作用域所有ViewModel对象
    viewModelMap?.values?.forEach { viewModel ->
      // 使用ObjectWatcher.expectWeaklyReachable
      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 {
        @Suppress("UNCHECKED_CAST")
        override fun  create(modelClass: Class): T =
          // 直接在storeOwner作用域实例化ViewModelClearedWatcher对象
          ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
      })
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
}

  • 由此可见:ViewModel的监测是在Activity.onCreate()/Fragment.onCreate()中分别创建一个自定义的ViewModel(即ViewModelClearedWatcher),然后监听ViewModelClearedWatcher的onCleared(),通过Hook ViewModelStore的方式获取相应应作用域(Activity/Fragment)中的所有ViewModel对象,并最终交给ObjectWatcher进行监控

2.2.3 RootViewWatcher

  • RootViewWatcher是处理RootView内存泄漏的监听器,由于系统并未提供全局RootView的监听,所以LeakCannary还是通过Hook方式来处理的,只不过这里没有自行Hook而是利用了square的另一个开源库curtains来处理的,主要通过Hook WMS 服务内部的 WindowManagerGlobal(sdk>16)或者 WindowManagerImpl(sdk<=16)来获取所有的RootView新增或移除的时机
/**
 * RootView泄漏监听,主要利用了Curtains库实现对
 */
class RootViewWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val listener = OnRootViewAddedListener { rootView ->
    // 判断rootView的窗口类型
    // 是否需要使用RootViewWatcher监听
    val trackDetached = when(rootView.windowType) {
      PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
          // Activities are already tracked by ActivityWatcher
          // 由于Activity已经有ActivityWatcher监听,这里直接返回false,即无需通过RootViewWatcher监听
          is Activity -> false
          is Dialog -> {
            // Use app context resources to avoid NotFoundException
            // https://github.com/square/leakcanary/issues/2137
            // 通过配置开启,默认不开启
            val resources = rootView.context.applicationContext.resources
            resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
          }
          // Probably a DreamService
          // 屏保等
          else -> true
        }
      }
      // Android widgets keep detached popup window instances around.
      POPUP_WINDOW -> false
      // Tooltip、Toast等进行监听
      TOOLTIP, TOAST, UNKNOWN -> true
    }
    if (trackDetached) {
      // 注册监听事件
      rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {

        val watchDetachedView = Runnable {
          // 接收发送的消息,使用ObjectWatcher对rootView进行监听
          reachabilityWatcher.expectWeaklyReachable(
            rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
          )
        }

        override fun onViewAttachedToWindow(v: View) {
          // 添加时移除消息
          mainHandler.removeCallbacks(watchDetachedView)
        }

        override fun onViewDetachedFromWindow(v: View) {
          // 监听RootView的移除事件,使用Handler发送消息处理
          mainHandler.post(watchDetachedView)
        }
      })
    }
  }

  override fun install() {
    // 注册RootView监听
    Curtains.onRootViewsChangedListeners += listener
  }

  override fun uninstall() {
    Curtains.onRootViewsChangedListeners -= listener
  }
}

  • 主要是针对Dialog(有监听开关配置)、DreamService、Tooltip、Toast等类型的RootView进行监听,通过调用rootView.addOnAttachStateChangeListener监听onViewDetachedFromWindow方法,监听RootView的移除事件,在移除事件中将RootView交给ObjectWatcher进行监控

2.2.4 ServiceWatcher

  • ServiceWatcher是处理Service内存泄漏的监听器,系统也并未提供全局Service的移除监听,所以LeaakCanary也是通过Hook方式实现的
@SuppressLint("PrivateApi")
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {

  private val servicesToBeDestroyed = WeakHashMap>()

  private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }

  private val activityThreadInstance by lazy {
    activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
  }

  private val activityThreadServices by lazy {
    val mServicesField =
      activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }

    @Suppress("UNCHECKED_CAST")
    mServicesField[activityThreadInstance] as Map
  }

  private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
  private var uninstallActivityManager: (() -> Unit)? = null

  override fun install() {
    checkMainThread()
    check(uninstallActivityThreadHandlerCallback == null) {
      "ServiceWatcher already installed"
    }
    check(uninstallActivityManager == null) {
      "ServiceWatcher already installed"
    }
    try {
      // Hook ActivityThread类中的mH.mCallback
      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback
          }
        }
        Handler.Callback { msg ->
          if (msg.obj !is IBinder) {
            return@Callback false
          }

          // 监听Service.onStop()事件消息
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            // activityThreadServices是通过反射获取的ActivityThread类中的mServices成员变量
            activityThreadServices[key]?.let {
              // 服务销毁前的处理,这里主要是暂存
              onServicePreDestroy(key, it)
            }
          }
          // Hook后继续执行Framework本身的逻辑
          mCallback?.handleMessage(msg) ?: false
        }
      }

      // Hook AMS IActivityManager
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        uninstallActivityManager = {
          swapActivityManager { _, _ ->
            activityManagerInstance
          }
        }
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          // 代理serviceDoneExecuting()方法
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              // 处理Service销毁,主要是将service交给ObjectWatcher进行监控
              onServiceDestroyed(token)
            }
          }
          // 继续执行serviceDoneExecuting()本身的方法
          try {
            if (args == null) {
              method.invoke(activityManagerInstance)
            } else {
              method.invoke(activityManagerInstance, *args)
            }
          } catch (invocationException: InvocationTargetException) {
            throw invocationException.targetException
          }
        }
      }
    } catch (ignored: Throwable) {
      SharkLog.d(ignored) { "Could not watch destroyed services" }
    }
  }

  override fun uninstall() {
    checkMainThread()
    uninstallActivityManager?.invoke()
    uninstallActivityThreadHandlerCallback?.invoke()
    uninstallActivityManager = null
    uninstallActivityThreadHandlerCallback = null
  }

  private fun onServicePreDestroy(
    token: IBinder,
    service: Service
  ) {
    servicesToBeDestroyed[token] = WeakReference(service)
  }

  private fun onServiceDestroyed(token: IBinder) {
    servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
      serviceWeakReference.get()?.let { service ->
        reachabilityWatcher.expectWeaklyReachable(
          service, "${service::class.java.name} received Service#onDestroy() callback"
        )
      }
    }
  }

  /**
   * Hook修改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?
    mCallbackField[mH] = swap(mCallback)
  }

  /**
   * Hook修改ActivityThread类中的mH.mCallback
   * swap 是一个 lambda 表达式,参数为 IActivityManager 的 Class 对象和接口原实现对象,返回值为注入的新对象
   */
  @SuppressLint("PrivateApi")
  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]

    val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)

    val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
    // 将swap的返回值作为新对象,实现 Hook
    mInstanceField[activityManagerSingletonInstance] =
      swap(iActivityManagerInterface, activityManagerInstance!!)
  }

  companion object {
    private const val STOP_SERVICE = 116

    private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
  }
}

  • Hook的大致步骤为:
    • Hook主线程消息循环的 mH.mCallback 回调,监听其中的STOP_SERVICE消息,将即将 Destroy 的 Service 对象暂存起来(由于 ActivityThread.H 中没有 DESTROY_SERVICE 消息,所以不能直接监听到 onDestroy() 事件,需要下面的步骤);
    • 使用动态代理 Hook AMS 与 App 通信的的 IActivityManager Binder 对象,代理其中的 serviceDoneExecuting() 方法,视为 Service.onDestroy() 的执行时机,拿到暂存的 Service 对象交给 ObjectWatcher 监控。

2.3 ObjectWatcher如何监控内存泄漏

2.3.1 Java四大引用相关知识

  • 强引用

    • 普通的最常见的引用,当对象具有强引用且未被置空,虚拟机即使OOM也不会回收
  • 软引用

    • 当内存充足时虚拟机不会回收它,内存不足时会自动回收,常用于图片缓存
    SoftReference softReference = new SoftReference<>(str);
    
    
  • 弱引用

    • 无论内存是否充足,只要手动调用垃圾回收System.gc()或等待虚拟机自动GC,弱引用就会被回收。主要用来放置在容易引起内存泄漏的位置,如Android中的Handler
    WeakReference weakReference = new WeakReference<>(str);
    
    
  • 虚引用

    • 虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。主要用来跟踪对象被垃圾回收器回收的活动,使用 PhantomReference 来创建
  • 引用队列ReferenceQueue

    • 当gc准备回收一个对象时,如果发现它还仅有软引用(或弱引用,或虚引用)指向它,就会在回收该对象之前,把这个软引用(或弱引用,或虚引用)加入到与之关联的引用队列(ReferenceQueue)中。如果一个软引用(或弱引用,或虚引用)对象本身在引用队列中,就说明该引用对象所指向的对象被回收了;
// 构造一个强引用  
Object obj = new Object();  
// 创建引用队列
ReferenceQueue referenceQueue = new ReferenceQueue<>();  
// 利用强引用和引用队列构造弱引用  
WeakReference weakReference = new WeakReference<>(obj, referenceQueue);  
System.out.println("调用GC前弱引用:" + weakReference.get()); // java.lang.Object@6108b2d7  
System.out.println("调用GC前引用队列:" + referenceQueue.poll()); // null  
  
// 将强引用手动置null  
obj = null;  
// 手动GC或虚拟机自动GC都会回收弱引用,这里手动调用GC  
System.gc();  
  
System.out.println("调用GC后弱引用:" + weakReference.get()); // null  
System.out.println("调用GC后引用队列:" + referenceQueue.poll()); // java.lang.Object@6108b2d7

 
  

2.3.2 ObjectWatcher对象监控

class ObjectWatcher constructor(
    private val clock: Clock,
    private val checkRetainedExecutor: Executor,
    private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {

    private val onObjectRetainedListeners = mutableSetOf()

    /** 被监控对象的映射表 */
    private val watchedObjects = mutableMapOf()

    /** 弱引用引用队列,与KeyedWeakReference相关联,对象正常销毁会存在这里面 */
    private val queue = ReferenceQueue()

    ...
    /** 监控对象泄漏 */
    @Synchronized
    override fun expectWeaklyReachable(
        watchedObject: Any,
        description: String
    ) {
        if (!isEnabled()) {
            return
        }
        // 移除watchedObjects中未泄漏的对象
        removeWeaklyReachableObjects()
        // 构造KeyedWeakReference 引用对象
        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
        // 默认5秒后执行检查
        checkRetainedExecutor.execute {
            moveToRetained(key)
        }
    }
    ...

    @Synchronized
    private fun moveToRetained(key: String) {
        removeWeaklyReachableObjects()
        // 移除watchedObjects中未泄露的对象后剩余的判定为发生泄漏
        val retainedRef = watchedObjects[key]
        if (retainedRef != null) {
            retainedRef.retainedUptimeMillis = clock.uptimeMillis()
            // 回调通知LeakCanary内部处理
            onObjectRetainedListeners.forEach { it.onObjectRetained() }
        }
    }

    /** 移除队列中未泄漏的对象 */
    private fun removeWeaklyReachableObjects() {
        var ref: KeyedWeakReference?
        do {
            ref = queue.poll() as KeyedWeakReference?
            if (ref != null) {
                watchedObjects.remove(ref.key)
            }
        } while (ref != null)
    }
}

/** 弱引用包装类 */
class KeyedWeakReference(
  /** 被监控对象 */
  referent: Any,
  /** 映射表的Key */
  val key: String,
  /** 描述 */
  val description: String,
  /** 监控开始时间(引用创建时间) */
  val watchUptimeMillis: Long,
  /** 关联的引用队列 */
  referenceQueue: ReferenceQueue
) : WeakReference(
  referent, referenceQueue
) {
  /** 判定对象为泄漏对象的时间,-1表示非泄漏对象或还未判定完毕 */
  @Volatile
  var retainedUptimeMillis = -1L

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

  companion object {
    /** 记录最近一次触发Heap Dump的时间 */
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L
  }
}

  • 主要经历了三个步骤
  1. 为被监控对象 watchedObject 创建一个 KeyedWeakReference 弱引用,并存储到 的映射表中;
  2. postDelay 五秒后检查引用对象是否出现在引用队列中,出现在队列则说明被监控对象未发生泄漏。随后,移除映射表中未泄露的记录,更新泄漏的引用对象的 retainedUptimeMillis 字段以标记为泄漏
  3. 通过回调 onObjectRetained 告知 LeakCanary 内部发生新的内存泄漏

2.4 Dump heap获取内存快照文件

  • ObjectWatcher 判定被监控对象发生泄漏后,会通过接口方法 OnObjectRetainedListener.onObjectRetained() 回调到 LeakCanary 内部的管理器 InternalLeakCanary
  • 由于分析工作比较耗时间,LeakCanary自然不会每次发现内存泄漏对象都进行分析工作,而会进行两个拦截:
  • 1. 泄漏对象计数未达到阈值,或者进入后台时间未达到阈值
  • 2. 计算距离上一次 HeapDump 未超过 60S
// InternalLeakCanary.kt
override fun onObjectRetained() = scheduleRetainedObjectCheck()

fun scheduleRetainedObjectCheck() {
  if (this::heapDumpTrigger.isInitialized) {
    heapDumpTrigger.scheduleRetainedObjectCheck()
  }
}

// HeapDumpTrigger.kt
fun scheduleRetainedObjectCheck(
  delayMillis: Long = 0L
) {
  val checkCurrentlyScheduledAt = checkScheduledAt
  // 避免重复postDelayed
  if (checkCurrentlyScheduledAt > 0) {
    return
  }
  checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
  backgroundHandler.postDelayed({
    checkScheduledAt = 0
    checkRetainedObjects()
  }, delayMillis)
}

private fun checkRetainedObjects() {
  val iCanHasHeap = HeapDumpControl.iCanHasHeap()

  val config = configProvider()

  if (iCanHasHeap is Nope) {
    if (iCanHasHeap is NotifyingNope) {
      // Before notifying that we can't dump heap, let's check if we still have retained object.
      // 泄漏计数
      var retainedReferenceCount = objectWatcher.retainedObjectCount
      // 泄漏计数>0时主动调用GC
      if (retainedReferenceCount > 0) {
        // 这个方法是调用RunTime.getRuntime().GC()并休眠等待100ms
        gcTrigger.runGc()
        // GC后再获取泄漏计数
        retainedReferenceCount = objectWatcher.retainedObjectCount
      }

      val nopeReason = iCanHasHeap.reason()
      // 这里会判断泄漏计数是否>5(默认阈值)
      val wouldDump = !checkRetainedCount(
        retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
      )

      if (wouldDump) {
        val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
        // 回调onEvent
        onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
        // 展示通知
        showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = uppercaseReason
        )
      }
    } else {
      SharkLog.d {
        application.getString(
          R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
        )
      }
    }
    return
  }

  var retainedReferenceCount = objectWatcher.retainedObjectCount

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

  if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  // 计算距离上一次HeapDump时间未超过60s会拦截
  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分析
  dumpHeap(
    retainedReferenceCount = retainedReferenceCount,
    retry = true,
    reason = "$retainedReferenceCount retained objects, app is $visibility"
  )
}

2.5 分析堆快照dumpHeap

  • LeakCanary 已经成功生成 .hprof 堆快照文件,并且发送了一个 LeakCanary 内部事件 HeapDump。LeakCanary 的配置项中设置了多个事件消费者EventListener,这三个会根据 App 当前的依赖项而选择最优的执行策略:
  • 1 - WorkerManager 多进程分析
  • 2 - WorkManager 异步分析
  • 3 - 异步线程分析(兜底策略)
// LeakCanary.kt
val eventListeners: List = listOf(
  LogcatEventListener,
  ToastEventListener,
  LazyForwardingEventListener {
    if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
  },
  when {
     // WorkManager多进程分析
      RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
        RemoteWorkManagerHeapAnalyzer
      // WorkManager 异步分析
      WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
      // 异步线程分析(兜底策略)
      else -> BackgroundThreadHeapAnalyzer
  }
),

2.5.1 WorkerManager 多进程分析

object RemoteWorkManagerHeapAnalyzer : EventListener {

  private const val REMOTE_SERVICE_CLASS_NAME = "leakcanary.internal.RemoteLeakCanaryWorkerService"

  // 这里通过RemoteLeakCanaryWorkerService这个类是否加载成功来判断
  // 是否有'com.squareup.leakcanary:leakcanary-android-process:2.9.1'这个依赖
  internal val remoteLeakCanaryServiceInClasspath by lazy {
    try {
      Class.forName(REMOTE_SERVICE_CLASS_NAME)
      true
    } catch (ignored: Throwable) {
      false
    }
  }

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      val application = InternalLeakCanary.application
      // 创建并分发 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()
      SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
      val workManager = WorkManager.getInstance(application)
      workManager.enqueue(heapAnalysisRequest)
    }
  }
}

internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
  RemoteListenableWorker(appContext, workerParams) {

  override fun startRemoteWork(): ListenableFuture {
    val heapDump = inputData.asEvent()
    val result = SettableFuture.create()
    heapAnalyzerThreadHandler.post {
      // 分析堆快照
      val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {
        result.isCancelled
      }) { progressEvent ->
        if (!result.isCancelled) {
          // 发送分析进度事件
          InternalLeakCanary.sendEvent(progressEvent)
        }
      }
      if (result.isCancelled) {
        SharkLog.d { "Remote heap analysis for ${heapDump.file} was canceled" }
      } else {
        // 发送分析完成事件
        InternalLeakCanary.sendEvent(doneEvent)
        result.set(Result.success())
      }
    }
    return result
  }

  override fun getForegroundInfoAsync(): ListenableFuture {
    return applicationContext.heapAnalysisForegroundInfoAsync()
  }
}

2.5.2 WorkManager异步分析

object WorkManagerHeapAnalyzer : EventListener {

  // 判断是否包含WorkManager
  internal val validWorkManagerInClasspath by lazy {
    try {
      Class.forName("androidx.work.WorkManager")
      val dataBuilderClass = Class.forName("androidx.work.Data$Builder")
      dataBuilderClass.declaredMethods.any { it.name == "putByteArray" }.apply {
        if (!this) {
          SharkLog.d { "Could not find androidx.work.Data$Builder.putByteArray, WorkManager should be at least 2.1.0." }
        }
      }
    } catch (ignored: Throwable) {
      false
    }
  }

  // setExpedited() requires WorkManager 2.7.0+
  private val workManagerSupportsExpeditedRequests by lazy {
    try {
      Class.forName("androidx.work.OutOfQuotaPolicy")
      true
    } catch (ignored: Throwable) {
      false
    }
  }

  internal fun OneTimeWorkRequest.Builder.addExpeditedFlag() = apply {
    if (workManagerSupportsExpeditedRequests) {
      setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    }
  }

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
        setInputData(event.asWorkerInputData())
        addExpeditedFlag()
      }.build()
      SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
      val application = InternalLeakCanary.application
      WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
    }
  }
}

internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
  Worker(appContext, workerParams) {
  override fun doWork(): Result {
    // 分析堆快照
    val doneEvent =
      AndroidDebugHeapAnalyzer.runAnalysisBlocking(inputData.asEvent()) { event ->
        // 发送分析进度事件
        InternalLeakCanary.sendEvent(event)
      }
    // 发送分析完成事件
    InternalLeakCanary.sendEvent(doneEvent)
    return Result.success()
  }
 ...
}

2.5.3 普通异步线程分析BackgroundThreadHeapAnalyzer

object BackgroundThreadHeapAnalyzer : EventListener {

  internal val heapAnalyzerThreadHandler by lazy {
    val handlerThread = HandlerThread("HeapAnalyzer")
    handlerThread.start()
    Handler(handlerThread.looper)
  }

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      heapAnalyzerThreadHandler.post {
        // 分析堆快照
        val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
          // 发送分析进度事件
          InternalLeakCanary.sendEvent(event)
        }
        // 发送分析完成事件
        InternalLeakCanary.sendEvent(doneEvent)
      }
    }
  }
}

  • 可以看到三种方式中最终LeakCanary 是通过子线程或者子进程执行 AndroidDebugHeapAnalyzer.runAnalysisBlocking 方法来分析堆快照的,并在分析过程中和分析完成后发送回调事件。
  • 堆快照分析最终是交给 Shark 中的 HeapAnalizer 完成的,这里就不再展开分析了,核心流程是:
  • 1、在堆快照中寻找泄漏对象,默认是寻找 KeyedWeakReference 类型对象;
  • 2、分析 KeyedWeakReference 对象的最短引用链,并按照引用链签名分组,按照 Application Leaks 和 Library Leaks 分类;
  • 3、返回分析完成事件。

3. 总结

  1. 利用ContentProvider进行初始化
  2. 注册 5 种 Android 泄漏场景的监控
    • Activity是利用application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)监听全局Activity生命周期
    • Fragment首先是通过 Application.registerActivityLifecycleCallbacks(…) 接口监听 Activity.onCreate()事件,再通过 FragmentManager.registerFragmentLifecycleCallbacks(…) 接口监听 Fragment 的生命周期
    • ViewModel是通过Hook ViewModelStore的方式实现的
    • Service是通过Hook mH.mCallback 回调,监听其中的STOP_SERVICE消息来实现的
    • RootView是通过借助curtains库Hook WMS 服务内部的 WindowManagerGlobal实现
  3. 收到销毁回调后,根据要回收对象创建 KeyedWeakReference 并关联 ReferenceQueue,延迟 5 秒检查相关对象是否被回收,如果未被回收则开启服务,dump heap 获取内存快照.hprof文件
  4. 通过 Shark 库解析.hprof 文件,获取泄漏对象,计算泄漏对象到 GC roots 的最短路径
  5. 合并多个泄漏路径并输出分析结果,将结果展示到可视化界面

你可能感兴趣的:(LeakCanary)