Android-开源框架分析-【LeakCanary】

2,429 阅读4分钟

前言

本文旨在梳理整个工作流程,文中源码基于 LeakCanary 2.7 版本。

简介

1.什么是LeakCanary

LeakCanary 是 square 公司开源的内存泄漏检测工具。

2.什么是内存泄漏

内存泄漏是指没有用的对象资源仍与 GcRoot 保持可达路径,导致系统无法进行回收,继而消耗资源。

流程解析

1.注册及寻找观察时机

LeakCanary 注册是利用 ContentProvider 的 onCreate 生命周期处于 Application 的 onCreate 生命周期之前的特性,将注册部分写在 ContentProvider 中,这样做免去手动写代码注册,但是会影响启动速度,所以一般在 debug 时使用。

internal sealed class AppWatcherInstaller : ContentProvider() {
  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }
}

AppWatcher 是初始化观察对象的入口处。

  @JvmOverloads
  fun manualInstall(
    application: Application,
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
    //此处添加默认的观察者
    watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
  ) {
    ...
    watchersToInstall.forEach {
      //依次初始化各个Watcher
      it.install()
    }
  }
  
  //添加到集合中的的观察者
  fun appDefaultWatchers(
      application: Application,
      reachabilityWatcher: ReachabilityWatcher = objectWatcher
   ): List<InstallableWatcher> {
     return listOf(
        ActivityWatcher(application, reachabilityWatcher),
        FragmentAndViewModelWatcher(application, reachabilityWatcher),
        RootViewWatcher(reachabilityWatcher),
        ServiceWatcher(reachabilityWatcher)
     )
  } 

manualInstall方法的参数watchersToInstall默认实现 Activity、Fragment、View、ViewModel 和 Service 的对象监听,接下来我们一一看看这几个 Watcher 是如何实现的。

1.1 ActivityWatcher

class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        //每个 activity onDestoryed 后,即可观察此对象后续是否有泄露风险
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

  //注册lifecycle生命周期回调
  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

不难看出 ActivityWatcher 通过application.ActivityLifecycleCallbacks监听 Activity 生命周期回调,在每个Activity.onDestory后调用expectWeaklyReachable方法(此方法后续解释)来观察对象是否被释放。

1.2 FragmentAndViewModelWatcher

FragmentAndViewModelWatcher 源码如下:

class FragmentAndViewModelWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
    
    //sdk在 8.0 及以上的 fragment
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
        //向 activity.fragmentManager 注册 fragmentLifecycler 在 onFragmentViewDestroyed 和 onFragmentDestroyed 后检查对象是否泄漏
        AndroidOFragmentDestroyWatcher(reachabilityWatcher)
      )
    }
    
    //androidX 包中的 fragment
    getWatcherIfAvailable(
      ANDROIDX_FRAGMENT_CLASS_NAME,
      ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      fragmentDestroyWatchers.add(it)
    }
    
    //support 包中的 fragment
    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?
      ) {
        for (watcher in fragmentDestroyWatchers) {
          watcher(activity)
        }
      }
    }
    
  //注册时先通过activity onActivityCreated 再来注册fragment
  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

Fragment 的 onDestory()onFragmentViewDestory() 监听。

internal class AndroidOFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

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

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

  override fun invoke(activity: Activity) {
    val fragmentManager = activity.fragmentManager
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }
}

1.3 RootViewWatcher

RootViewWatcher 源代码如下:

class RootViewWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val listener = OnRootViewAddedListener { rootView ->
    //通过区分 rootview 的类型,判断是否需要检测,  
    //假如写一个单例的Dialog仅使用show()和hide()控制,而不会销毁对象,不属于泄漏)
    val trackDetached = when(rootView.windowType) {
      PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
          is Activity -> false
          is Dialog -> rootView.resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
          else -> true
        }
      }
      POPUP_WINDOW -> false
      TOOLTIP, TOAST, UNKNOWN -> true
    }

    if (trackDetached) {
      //在rootView detachedFromWindow 开始检测是否泄漏
      rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {

        val watchDetachedView = Runnable {
          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)
        }
      })
    }
  }

  override fun install() {
    Curtains.onRootViewsChangedListeners += listener
  }

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

通过OnAttachStateChangeListeneronViewAttachedToWindowonViewDetachedFromWindow方法回调可在后续做内存泄漏的检查工作。

1.4 ServiceWatcher

Service 通过 hook ActivityThread 的 H 类和 AMS,当 AMS 调用serviceDoneExecuting方法后检测是否有泄漏的风险。

class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
   ...
  override fun install() {
    ...
    try {
    //hook ActivityThread中H类的Callback 
      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback
          }
        }
        //在收到STOP_SERVICE时将Service以弱应用形式记录
        Handler.Callback { msg ->
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            activityThreadServices[key]?.let {
              onServicePreDestroy(key, it)
            }
          }
          mCallback?.handleMessage(msg) ?: false
        }
      }
      //这里hook了AMS
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        uninstallActivityManager = {
          swapActivityManager { _, _ ->
            activityManagerInstance
          }
        }
        //通过代理形式 在调用serviceDoneExecuting方法时可判断对象是否泄漏
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              onServiceDestroyed(token)
            }
          }
          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 onServiceDestroyed(token: IBinder) {
    servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
      serviceWeakReference.get()?.let { service ->
        reachabilityWatcher.expectWeaklyReachable(
          service, "${service::class.java.name} received Service#onDestroy() callback"
        )
      }
    }
  }
  ...省略了hook代码部分
 }

1.5 小结

上述主要是添加需要检测的对象,以及寻找每个对象的回收时机点:

  • Activity: Activity#onActivityDestroyed
  • Fragment: Fragment#onDestroy()
  • FragmentView: Fragment#onDestoryView()
  • ViewModel: ViewModle#onClear()
  • RootView: addOnAttachStateChangeListener#onViewDetachedFromWindow
  • Service: AMS#serviceDoneExecuting 假如我们有个别自己需要观察的对象,即可在对象销毁后通过expectWeaklyReachable来检测是否有泄露风险。

2. 寻找泄露对象-ObjectWatcher

当对象在恰当的时机回收后,通过ObjectWatcherexpectWeaklyReachable方法进行观察是否有泄露风险

  @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    //先移除一次非泄漏的弱引用对象
    removeWeaklyReachableObjects()
    
    val key = UUID.randomUUID().toString()
    val watchUptimeMillis = clock.uptimeMillis()
    
    //将观察对象包成 weakRefrence 对象,并注册到给定的队列中
    //注:弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
    val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    watchedObjects[key] = reference
    
    //异步检查观察对象
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }
  
  //5s后再次检查观察对象是否存活 
  fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
  ) {
    ...
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      checkRetainedObjects()
    }, delayMillis)
  }

再一次检查泄露对象

  private fun checkRetainedObjects() {
    ...
    //retainedObjectCount.get()会再次移除弱引用对象并得出未被移除的数量
    var retainedReferenceCount = objectWatcher.retainedObjectCount
    
    //存在泄漏对象的话手动GC一次 然后再次检测数量
    if (retainedReferenceCount > 0) {
      gcTrigger.runGc()
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }
    
    //如果泄漏数量不超出阈值(默认5) return
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
    ...
    //dump堆文件 
    dumpHeap(
      retainedReferenceCount = retainedReferenceCount,
      retry = true,
      reason = "$retainedReferenceCount retained objects, app is $visibility"
    )
  }
  

这一步先触发 GC 回收对象,再次判断未回收的对象数量,如果超出默认数量5个,通过 Android 的方式生成 hprof 文件,再通过 HeapAnalyzerService 分析这个文件。

private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean,
    reason: String
  ) {
    ...
    //这里通过 Android 原生方法 Debug.dumpHprofData(heapDumpFile.absolutePath);获取文件
    when (val heapDumpResult = heapDumper.dumpHeap()) {
      is NoHeapDump -> {
        ...
      }
      is HeapDump -> {
        lastDisplayedRetainedObjectCount = 0
        lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
        objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
        //得到dump文件后 开始分析dump文件
        HeapAnalyzerService.runAnalysis(
          context = application,
          heapDumpFile = heapDumpResult.file,
          heapDumpDurationMillis = heapDumpResult.durationMillis,
          heapDumpReason = reason
        )
      }
    }
  }
  
  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)
      }
      startForegroundService(context, intent)
    }

HeapAnalyzerService 属于是 square 的另一个开源库 shark,是一个前台服务,整个流程通过analyzeHeap方法分析文件,将分析出的结果包装成一个 HeapAnalysisSuccess 对象通过onHeapAnalyzedListener回调。

3. 分析Hprof

hprof 文件的标准协议主要由 head 和 body 组成,body 是由一系列不同类型的 Record 组成,Record 主要用于描述 trace、object、thread 等信息,依次分为4个部分:TAG、TIME、LENGTH、BODY,其中 TAG 就是表示 Record 类型。Record 之间依次排列或嵌套,最终组成 hprof 文件。

多个 Record 被进抽象为 HprofMemoryIndex,Index 可以快速定位到对应对象在 hprof 文件中的位置;最终 Index 和 Hprof 一起再组成 HprofGraph,graph 做为 hprof 的最上层描述,将所有堆中数据抽象为了 gcRoots、objects、classes、instances 等集合。

interface HeapGraph {
  val gcRoots: List<GcRoot>

  val objects: Sequence<HeapObject>

  val classes: Sequence<HeapClass>

  val instances: Sequence<HeapInstance>

  val objectArrays: Sequence<HeapObjectArray>

  ...
}

后续通过 Graph 的 objects 集合找出泄露对象,然后通过广度优先遍历找出其到 GcRoot 的引用路径链,结束流程。

总结

LeakCanary 通过 ContentProvider 注册,在对象被销毁时利用 WeakRefrence + RefrenceQueue + GC观察泄露对象,dump hprof文件,通过shark库分析文件,查找泄露对象引用路径。
通过对于整体流程的理解后,可知 LeakCanary 并不适合于线上使用,在其使用时间会导致频繁 GC,生成分析 hprof 文件都会占用资源进而影响用户,且 hprof 文件一般都很大,上传前还需要进一步压缩。