LeakCanary2.4源码分析

1,734 阅读7分钟

前言

LeakCanary是一个Android端用于内存泄露检测开源工具,可以很方便的自动检测销毁的Activity、Fragment的实例、Fragment中View的实例、清除的ViewModel实例的内存泄露。本篇基于2.4版本进行分析。

LeakCanary官网地址: square.github.io/leakcanary/

LeakCanary的使用

在2.0之后,LeakCanary只需要在build.gradle文件中添加leakcanary-android依赖即可.

  dependencies {
    // debugImplementation because LeakCanary should only run in debug builds.
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
   }

添加依赖后,运行应用即可自动检测应用中的内存泄露,主要检测泄露对象为:

  • destroyed Activity instances: 销毁的Activity实例
  • destroyed Fragment instances: 销毁的Fragment实例
  • destroyed fragment View instances: 销毁的fragment中View实例
  • cleared ViewModel instances: 清除的ViewModel实例

会在桌面上生成Leaks应用图标,用于展示内存泄露详情;同时你也可以使用logcat中查看TAG为LeakCanary的日志信息。

2.4版本也增加了一个新的plumber-android的库,解决已知的Android leaks问题,使用时直接使用implementation集成;使用Shark CLI也可以检测连接设备上任意应用程序的内存泄漏问题。

LeakCanary的基本原理

1.介绍

什么是内存泄漏? 在java运行时,内存泄漏是导致应用程序持有一个不再需要的对象引用的程序问题,它会导致分配给它的内存无法回收,进而会导致内存溢出问题。

导致内存泄漏的一般原因? 大部分的内存泄漏是跟对象的生命周期有关的bug,例如:

  • 添加一个Fragment实例到backstack而没有在onDestroyView中清除Fragment的view.
  • 注册一个关联生命周期对象的listener,broadcast receiver或者订阅了一个RxJava,而没有反注册.

为什么需要使用LeakCanary? LeakCanary能够帮助我们在开发中检测和查找隐藏的内存泄漏,这样可以协助我们在开发中就定位和解决这些泄漏问题,减少应用OOM crash问题。

2.LeakCanary如何工作

LeakCanary安装后,它通过4个步骤自动监测和报告内存泄漏:

  • 1.Detecting retained objects.
  • 2.Dumping the heap.
  • 3.Analyzing the heap.
  • 4.Categorizing leaks.

I.Detecting retained objects

LeakCanary利用Android lifecycle回调,在Activity、Fragment等的onDestroyed方法中自动监测这些实例对象,将实例传递给ObjectWatcher,并使用WeakReference来持有它们。当onDestroy方法调用后等待5seconds并执行gc后,被观察的对象依然保留,则可能存在内存泄露;LeadCanary等待被保留可能内存泄露对象达到阈值5个时执行dumping the heap,并展示一个通知。

II.Dumping the heap

当保留对象达到阈值5个时,LeakCanary执行dump将java堆栈信息存入.hprof文件,执行dump将会花费一些时间,LeakCanary会显示一个Toast。

LeakCanary在哪里存储hprof文件?

如果应用已经获取了android.permission.WRITE_EXTERNAL_STORAGE权限,则会在Download目录下生成一个leakcanary-**(package name)的文件夹来存储dump的hprof文件;如果未获得权限,则会显示一个通知去获取权限。

III.Analyzing the heap

LeakCanary使用Shark库来分析.hprof文件并定位保留的对象位置;对于每个被保留对象,LeakCanary将找到它被持有,不被gc回收的泄露轨迹leak trace;分析结束,将展示一个通知概要,Leakcanary为每个leak trace生成一个签名signature(签名根据怀疑导致泄露的相关对象名称字符串计算sha1Hash得来),并根据签名来将检测到的泄露进行分组。点击通知将展示泄露详情。对于第一次出现的leak signature,将标记New标签。

Finding retainded objects -> Building leak traces -> Found *retained objects grouped as * distinct leaks Tap for more details

IV.Categorizing leaks

LeakCanary将找到的泄漏分为两类:Application Leaks和Library Leaks,可以在Logcat中查看;Library Leaks是由第三方库导致的已知内存泄露bug,会添加Library标签,可能不在我们应用控制范围;

3.解决内存泄露

内存泄露是导致应用程序持有一个本不再需要的对象的程序问题。下面分为4个步骤去解决内存泄露:

1.Find the leak trace. 2.Narrow down the suspect references. 3.Find the reference causing the leak. 4.Fix the leak. 前两步LeakCanary帮我们搞定了,后面两步需要我们自己去解决;

I.查找泄漏轨迹(Find the leak trace)

一个泄漏轨迹是从GC Roots到保留对象的一个最短强引用路径;有4类主要的GC Roots:

  • Local variables: 属于线程方法栈的本地变量
  • active Java threads: java线程实例
  • System Classes: 系统类(不会卸载)
  • Native references: 本地

II.缩小怀疑引用(Narrow down the suspect references)

LeakCanary将检查leak trace路径上的怀疑对象的状态和生命周期来确定这些对象是否泄露;你可以自定义ObjectInspector去定义LeakCanary的工作方式。

III.查找导致泄露的引用(Find the reference causing the leak)

根据经验进一步排除leak trace上可能导致泄露的对象。

IV.解决泄露(Fix the leak)

找到泄露的对象引用后,你需要确定具体引用对象是什么,它应该什么时候被销毁但没有销毁;有时候很明显,但有时候你需要更多信息才能确认;你可以添加标签或者直接通过分享hprof去确定。

4.LeakCanary初始化

leakcanary-object-watcher-android结构图
查看LeakCanary源码,可以看到AppWatcher中manualInstall方法,里面调用了InternalAppWatcher的install方法来初始化应用的Activity、Fragment等泄露观察。 AppWatcher.kt

fun manualInstall(application: Application) {
    InternalAppWatcher.install(application)
}

I.AppWatcher.manualInstall()在主进程中被自动调用

如何自动被调用并初始化的? 该方法在AppWatcherInstaller的OnCreate()方法中调用,而AppWatcherInstall继承自ContentProvider并在AndroidManifest.xml中注册,利用ContentProvider在无需显示初始化;ContentProvider的onCreate的调用时机介于Application的attachBaseContext和onCreate之间,Provider的onCreate优先于Application的onCreate执行,并且此时的Application已经创建成功,而Provider里的context正是Application的对象。

LeakCanary2.4中在values.xml中增加了配置字段:false 来控制是否自动初始化,并在AndroidManifest.xml中配置。

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

如何指定被调用的进程?

AppWatcherInstaller作为密封类里有两个子类MainProcess、LeakCanaryProcess,分别指定LeakCanary运行的进程;使用时分别对应leakcanary-android和leakcanary-android-process依赖。

InternalAppWatcher.install()代码

fun install(application: Application) {
    checkMainThread()
    if (this::application.isInitialized) {
    return
    }
    SharkLog.logger = DefaultCanaryLog()
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }  //AppWatcher配置信息,可以随时更新
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)   //初始化Activity对象监测
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)   //初始化Fragment对象监测
    onAppWatcherInstalled(application)
}

如何更新AppWatcher配置?

    Kotlin:  AppWatcher.config = AppWatcher.config.copy(watchFragmentViews = false)
    Java:    AppWatcher.Config config = AppWatcher.getConfig().newBuilder()
                                        .watchFragmentViews(false)
                                        .build();
            AppWatcher.setConfig(config);

5.LeakCanary如何监测Activity、Fragment内存泄露?

关键代码如下,分别对Activity和Fragment进行监测,内部实现原理相似.

ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)

先来分析ActivityDestroyWatcher.install(application, objectWatcher, configProvider):

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

 private val lifecycleCallbacks =
 object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
  override fun onActivityDestroyed(activity: Activity) {    //只监听onActivityDestroyed方法
    if (configProvider().watchActivities) {                 //如果配置中监测Activity,则调用ObjectWatcher.watch来检测Activity
      objectWatcher.watch(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
      )
    }
  }
}

companion object {   //伴生对象,类似java中的静态方法
    fun install(
     application: Application,
     objectWatcher: ObjectWatcher,
    configProvider: () -> Config
 ) {
    val activityDestroyWatcher =
     ActivityDestroyWatcher(objectWatcher, configProvider)  //类似静态方法中初始化实例
    application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)   //调用application注册Activity的生命周期回调监听
    }
 }
}

再来分析FragmentDestroyWatcher.install(application, objectWatcher, configProvider): FragmentDestroyWatcher中主要是install()方法

 fun install(
    application: Application,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
) {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()       //fragmentDestroyWatchers列表,支持不同Fragment实例的检测;这里的watcher都继承自(Activity)->Unit表示方法类型/函数类型,参数为Activity,返回值为空;因为是方法类型所以需要重写invoke方法

if (SDK_INT >= O) {                                                     //Android O后构建AndroidOFragmentDestroyWatcher
  fragmentDestroyWatchers.add(
      AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
  )
}

getWatcherIfAvailable(                                      //如果Class.for(className)能找到androidx.fragment.app.Fragment和
    ANDROIDX_FRAGMENT_CLASS_NAME,                           //leakcanary.internal.AndroidXFragmentDestroyWatcher则添加AndroidXFragmentDestroyWatcher则添加
    ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
    objectWatcher,
    configProvider
)?.let {
  fragmentDestroyWatchers.add(it)
}

getWatcherIfAvailable(                                  //如果Class.for(className)能找到android.support.v4.app.Fragment和
    ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,                //leakcanary.internal.AndroidSupportFragmentDestroyWatcher则添加AndroidSupportFragmentDestroyWatcher
    ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
    objectWatcher,
    configProvider
)?.let {
  fragmentDestroyWatchers.add(it)
}

if (fragmentDestroyWatchers.size == 0) {
  return
}

application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
  override fun onActivityCreated(           //注册Activity生命周期回调,在Activity的onActivityCreated()方法中遍历这些watcher方法类型,实际调用的是对应的invoke方法
    activity: Activity,
    savedInstanceState: Bundle?
  ) {
    for (watcher in fragmentDestroyWatchers) {      
      watcher(activity)
    }
  }
})
}

如果系统是Android O以后版本,使用AndroidOFragmentDestroyWatcher,如果app使用的是androidx中的fragment,则添加对应的AndroidXFragmentDestroyWatcher,如果使用support库中的fragment,则添加AndroidSupportFragmentDestroyWatcher。最终在invoke方法中使用对应的fragmentManager注册Fragment的生命周期回调,在onFragmentViewDestroyed()和onFragmentDestroyed()方法中使用ObjectWatcher来检测fragment。下面以AndroidXFragmentDestroyWatcher为例:

internal class AndroidXFragmentDestroyWatcher(
 private val objectWatcher: ObjectWatcher,
 private val configProvider: () -> Config
) : (Activity) -> Unit {

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

    override fun onFragmentCreated(     //在AndroidXFragmentDestroyWatcher中添加了onFragmentCreated()回调,添加了ViewModelStoreOwner为Fragment的ViewModelClearedWatcher监测
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      ViewModelClearedWatcher.install(fragment, objectWatcher, configProvider)
    }

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null && configProvider().watchFragmentViews) {
        objectWatcher.watch(
            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
    ) {
      if (configProvider().watchFragments) {
        objectWatcher.watch(
            fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
        )
      }
    }
     }

    override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {        //此处根据不同FragmentActivity和Fragment,取得对应的FragmentManager,添加生命周期回调
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      ViewModelClearedWatcher.install(activity, objectWatcher, configProvider)  //添加了ViewModelStoreOwner为Activity的ViewModelClearedWatcher监测
    }
 }
}

ViewModelClearedWatcher继承自ViewModel,里面使用viewModelMap来存储ViewModelStoreOwner中的ViewModel,并使用伴生对象来初始化自己,关联到ViewModelStoreOwner;在onCleared()方法中使用ObjectWatcher来监测。 综上,最后都是通过ObjectWatcher.watch()来监测Activity、Fragment、Fragment的View、ViewModel。下面重点分析ObjectWatcher.watch()方法:

  private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()

  /**
   * References passed to [watch].
   */
  private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()       
  
  private val queue = ReferenceQueue<Any>()

  /**
   * Watches the provided [watchedObject].
   *
   * @param description Describes why the object is watched.
   */
  @Synchronized fun watch(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
    removeWeaklyReachableObjects()     //1.移除watchedObjects中弱可达对象引用(将被回收)
    val key = UUID.randomUUID()
        .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    val reference =                      
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)  //2.将要观察的对象放入KeyedWeakReference中
    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    //3.存入watchedObjects
    checkRetainedExecutor.execute {    //4.使用checkRetainedExecutor定时执行moveToRetained()
      moveToRetained(key)
    }
  }
  
 @Synchronized private fun moveToRetained(key: String) {  //5.moveToRetained()中将再次执行removeWeaklyReachableObjects(),确保没有遗漏,然后将未弱可达及未回收的对象取出回调其ObjectWatcher的onObjectRetained()方法;
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }
  
  private fun removeWeaklyReachableObjects() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }

OnObjectRetainedListener是在InternalLeakCanary的invoke()方法中通过 AppWatcher.objectWatcher添加的监听,最终回调到InternalLeakCanary的onObjectRetained()方法,里面回调heapDumpTrigger.onObjectRetained()方法,然后调用scheduleRetainedObjectCheck("found new object retained")

 private fun scheduleRetainedObjectCheck(reason: String) {
    if (checkScheduled) {
      SharkLog.d { "Already scheduled retained check, ignoring ($reason)" }
      return
    }
    checkScheduled = true
    backgroundHandler.post {            //在后台线程LeakCanary-Heap-Dump中执行
      checkScheduled = false
      checkRetainedObjects(reason)
    }
  }
  
  
  
private fun checkRetainedObjects(reason: String) {
 val config = configProvider()
// A tick will be rescheduled when this is turned back on.
if (!config.dumpHeap) {
  SharkLog.d { "No checking for retained object: LeakCanary.Config.dumpHeap is false" }
  return
}
SharkLog.d { "Checking retained object because $reason" }

var retainedReferenceCount = objectWatcher.retainedObjectCount

if (retainedReferenceCount > 0) {   //如果retained的引用数量>0,执行gc,更新retained引用数量
  gcTrigger.runGc()
  retainedReferenceCount = objectWatcher.retainedObjectCount
}

if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return  //当retaind的引用数量<  默认阈值5时,只弹通知不做dump处理

if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {  //如果正在debug,默认debug时不进行heap dump
  showRetainedCountWithDebuggerAttached(retainedReferenceCount)             //显示debug时的retained对象数量通知
  scheduleRetainedObjectCheck("debugger was attached", WAIT_FOR_DEBUG_MILLIS)
  SharkLog.d {
      "Not checking for leaks while the debugger is attached, will retry in $WAIT_FOR_DEBUG_MILLIS ms"
  }
  return
}

SharkLog.d { "Found $retainedReferenceCount retained references, dumping the heap" }
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis   //更新headpDump时间
dismissRetainedCountNotification()                              //取消显示的通知
val heapDumpFile = heapDumper.dumpHeap()             
if (heapDumpFile == null) {
  SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
  scheduleRetainedObjectCheck("failed to dump heap", WAIT_AFTER_DUMP_FAILED_MILLIS)   //dump失败后5秒钟再进行check
  showRetainedCountWithHeapDumpFailed(retainedReferenceCount)   //显示dump heap失败的通知
  return
}
lastDisplayedRetainedObjectCount = 0
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)   //移除heapDumpUptimeMillis之前的被观察对象创建的KeyedWeakReference

HeapAnalyzerService.runAnalysis(application, heapDumpFile)   //启动服务HeapAnalyzerService(SDK版本>=26启动前台服务,<26启动服务)进行分析
}

HeapAnalyzerService中使用HeapAnalyzer调用analyze方法进行hprof文件的分析,具体使用的就是shark库分析,不再使用之前版本中使用的haha库了。