浅谈LeakCanary2源码

Posted 码农乐园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈LeakCanary2源码相关的知识,希望对你有一定的参考价值。

LeakCanary是一个内存泄漏检测的工具。那么,内存泄漏是如何定义的,通常有哪些情况呢?
一,在基于 Java 的运行时中,内存泄漏是一种编程错误,它导致应用程序保留对不再需要的对象的引用。因此,为该对象分配的内存无法回收,最终导致OutOfMemoryError (OOM)崩溃。

二,大多数内存泄漏是由与对象生命周期相关的错误引起的。以下是一些常见的 android 错误:

  • Fragment实例添加到 backstack 而不清除该 Fragment 的视图字段Fragment.onDestroyView()(在此 StackOverflow 答案中的更多详细信息)。

  • Activity实例作为Context字段存储在由于配置更改而在活动重新创建中幸存下来的对象中。

  • 注册一个侦听器、广播接收器或 RxJava 订阅,这些订阅引用具有生命周期的对象,并且在生命周期结束时忘记注销。

LeakCanary2和LeakCanary的区别

LeakCanary 2.0使用非常简单,只需要在build.gradle中配置一下,无需在项目中添加任何代码。


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

原理就是利用ContentProvider的特性,其onCreate方法会在Application的onCreate方法之前被系统调用。所以只需要在AndroidManifest.xml中配置一下这个ContentProvider,然后在onCreate方法中进行初始化即可。

ContentProvider除了在onCreate方法中进行了初始化处理,其他方法都是空实现。在上面的第26行,onCreate方法中初始化代码为InternalAppWatcher.install(application)

InternalAppWatcher.install(Application)方法完成了LeakCanary的初始化操作,里面涉及到协作的一些类的定义。

leakcanary-object-watcher-android/src/main/java/leakcanary/internal/InternalAppWatcher.kt


 
internal object InternalAppWatcher {


  // 通过判断lateinit修饰的Application是否已经初始化,来判断LeakCanary是否已经安装
  val isInstalled
    get() = ::application.isInitialized


  // LeakCanary安装完成的回调,实际上对应的是leakcanary.internal.InternalLeakCanary这个类
  private val onAppWatcherInstalled: (Application) -> Unit


  val isDebuggableBuild by lazy {
    (application.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
  }


  lateinit var application: Application


  // ObjectWatcher参数1
  private val clock = object : Clock {
    override fun uptimeMillis(): Long {
      return SystemClock.uptimeMillis()
    }
  }


  private val mainHandler = Handler(Looper.getMainLooper())


  init {
    // 这里为什么要这么费劲的获取该object的对象呢?
    // 因为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
  }


  // 主线程抛出任务,delay 5s
  private val checkRetainedExecutor = Executor {
    mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis)
  }
  val objectWatcher = ObjectWatcher(
      clock = clock,
      checkRetainedExecutor = checkRetainedExecutor,
      isEnabled = { AppWatcher.config.enabled }
  )


  fun install(application: Application) {
    ...
    checkMainThread()
    // lateinit修饰的对象可以通过::<field>.isInitialized来判断有没有初始化
    // 如果已经初始化了,则不需要再次install
    if (this::application.isInitialized) {
      return
    }
    InternalAppWatcher.application = application


    // 安装ActivityDestroyWatcher、FragmentDestroyWatcher
    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    // 通知上层模块的InternalLeakCanary.invoke方法
    onAppWatcherInstalled(application)
  }
  ...
}

在上面的这个install方法中,干了三件事:1. 初始化一个ObjectWatcher对象:其clock就是SystemClock.uptimeMillis()checkRetainedExecutor是利用主线程Handler执行的任务 2. Activity、Fragment销毁的观察者的安装 3. 通知InternalLeakCanary,watcher已经安装完成

由于Activity、Fragment的销毁不会立刻发生,所以我们先看看第3点中,InternalLeakCanary做了什么工作。
InternalLeakCanary的部分声明为object InternalLeakCanary : (Application) -> Unit,所以它到底实现了什么呢。这其实对应kotlin中Functions.kt文件的Function1接口,P1为Application,R为Unit即void:

/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}


所以接着看InternalLeakCanary.invoke方法即可,该方法也是完成了一些初始化操作,如下:
InternalLeakCanary.kt
override fun invoke(application: Application) {
  this.application = application


  // 内存泄漏时回调该类的方法
  AppWatcher.objectWatcher.addOnObjectRetainedListener(this)


  val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)


  // gcTrigger使用的默认的
  val gcTrigger = GcTrigger.Default


  val configProvider = { LeakCanary.config }


  // HandlerThread + Handler 来处理后台任务
  val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
  handlerThread.start()
  val backgroundHandler = Handler(handlerThread.looper)


  heapDumpTrigger = HeapDumpTrigger(
      application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
      configProvider
  )
  // 自定义的扩展方法来检测App是否可见(处于前台)
  application.registerVisibilityListener { applicationVisible ->
    this.applicationVisible = applicationVisible
    heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
  }
  // 动态添加Shortcut
  addDynamicShortcut(application)


  disableDumpHeapInInstrumentationTests()
}

其实上面这些初始化的变量,大部分我们都不会在检测内存泄漏时遇到,这些变量大部分都与dump heap有关。
这里展开说说其中的“自定义的扩展方法来检测App是否可见(处于前台)”这里面的实现原理,这个需求在日常开发中也会用到,也是通过向Application注册Activity生命周期回调,通过计算start-stop的Activity个数来实现的:

leakcanary-android-core/src/main/java/leakcanary/internal/VisibilityTracker.kt


 
internal class VisibilityTracker(
  private val listener: (Boolean) -> Unit
) :
    Application.ActivityLifecycleCallbacks by noOpDelegate() {


  private var startedActivityCount = 0


  /**
   * Visible activities are any activity started but not stopped yet. An activity can be paused
   * yet visible: this will happen when another activity shows on top with a transparent background
   * and the activity behind won't get touch inputs but still need to render / animate.
   */
  private var hasVisibleActivities: Boolean = false


  override fun onActivityStarted(activity: Activity) {
    startedActivityCount++
    if (!hasVisibleActivities && startedActivityCount == 1) {
      hasVisibleActivities = true
      listener.invoke(true)
    }
  }


  override fun onActivityStopped(activity: Activity) {
    // This could happen if the callbacks were registered after some activities were already
    // started. In that case we effectively considers those past activities as not visible.
    if (startedActivityCount > 0) {
      startedActivityCount--
    }
    if (hasVisibleActivities && startedActivityCount == 0 && !activity.isChangingConfigurations) {
      hasVisibleActivities = false
      listener.invoke(false)
    }
  }
}


internal fun Application.registerVisibilityListener(listener: (Boolean) -> Unit) {
  registerActivityLifecycleCallbacks(VisibilityTracker(listener))
}

上面就是LeakCanary的初始化代码了,接下来说说Activity与Fragment是如何检测内存泄漏的。

如何观察Activity、Fragment

在上一节LeakCanary2初始化中,我们还没有讲解下面两行代码:

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

这两行代码都是通过向Application注册registerActivityLifecycleCallbacks从而获取每个启动的Activity,然后

  • 对于Activity而言,直接观察Activity即可

  • 对于Fragment而言,由于Fragment需要依附于Activity,且需要从Activity中获取FragmentManager,然后通过其registerFragmentLifecycleCallbacks方法观察Fragment

上面两行代码就是这个实现逻辑。先看看Activity的观察逻辑:

leakcanary-object-watcher-android/src/main/java/leakcanary/internal/ActivityDestroyWatcher.kt


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


  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      // noOpDelegate()是一个动态代理的实现,不过里面没有写任何逻辑 所以是no op
      // 此处相当于一个适配器方法
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
          objectWatcher.watch(activity)
        }
      }
    }


  companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
  }
}

上面的逻辑很简单,就是对于每个Activity,在其onDestroy方法调用之后,调用objectWatcher.watch观察这个Activity。该方法的逻辑我们下一节再说。

然后我们看看Fragment里面的观察逻辑,由于Fragment有两种:1. android.app.Fragment 2. support包里面的androidx.fragment.app.Fragment

前者的FragmentLifecycleCallbacks有API Level限制,限制为O;后者则没有API限制了,但是有androidx限制。所以FragmentDestroyWatcher会判断这两个的是否满足条件,满足条件后才会进行观察。

leakcanary-object-watcher-android/src/main/java/leakcanary/internal/FragmentDestroyWatcher.kt


 
/**
 * Internal class used to watch for fragments leaks.
 */
internal object FragmentDestroyWatcher {


  private const val ANDROIDX_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"
  private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
    "leakcanary.internal.AndroidXFragmentDestroyWatcher"


  fun install(
    application: Application,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
  ) {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()


    // 如果SDK大于等于O,则添加android.app.Fragment的观察者
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
          AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
      )
    }


    // 通过反射判定androidx.fragment.app.Fragment以及其观察者是否存在
    if (classAvailable(ANDROIDX_FRAGMENT_CLASS_NAME) &&
        classAvailable(ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME)
    ) {
      // 反射实例化androidx Fragment的观察者并添加到里面list里面
      val watcherConstructor = Class.forName(ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME)
          .getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java)
      @kotlin.Suppress("UNCHECKED_CAST")
      fragmentDestroyWatchers.add(
          watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit
      )
    }


    // 如果watcher为空,则不需要进行观察了
    if (fragmentDestroyWatchers.size == 0) {
      return
    }


    // 对每个Activity里面的所有的Fragment进行观察
    application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        for (watcher in fragmentDestroyWatchers) {
          watcher(activity)
        }
      }
    })
  }


  private fun classAvailable(className: String): Boolean {
    return try {
      Class.forName(className)
      true
    } catch (e: ClassNotFoundException) {
      false
    }
  }
}

AndroidOFragmentDestroyWatcherAndroidXFragmentDestroyWatcher两者的源码非常类似,只是针对的Fragment不同而调用的API不同而已,下面以AndroidXFragmentDestroyWatcher为例看看里面是如何实现的。

leakcanary-object-watcher-android-androidx/src/main/java/leakcanary/internal/AndroidXFragmentDestroyWatcher.kt


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


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


    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null && configProvider().watchFragmentViews) {
        objectWatcher.watch(view)
      }
    }


    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      if (configProvider().watchFragments) {
        objectWatcher.watch(fragment)
      }
    }
  }


  override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
    }
  }
}

实现就是向Activity的FragmentManager注册FragmentLifecycleCallbacks,这样在Fragment调用onDestroyViewonDestory之后就能观察Fragment的View或者Fragment本身了。

内存泄漏判定

现在我们来看看ObjectWatcher.watch(Any)方法,在上面一节中我们看到,Activity、Fragment的View、Fragment都是由该方法进行观察的,所以最后还是统一回到了这里。

leakcanary-object-watcher/src/main/java/leakcanary/ObjectWatcher.kt


 
/**
  * Identical to [watch] with an empty string reference name.
  */
@Synchronized fun watch(watchedObject: Any) {
  watch(watchedObject, "")
}


/**
  * Watches the provided [watchedObject].
  *
  * @param name A logical identifier for the watched object.
  */
@Synchronized fun watch(
  watchedObject: Any,
  name: String
) {
  if (!isEnabled()) {
    return
  }
  // 将ReferenceQueue中出现的弱引用移除
  // 这是一个出现频率很高的方法,也是内存泄漏检测的关键点之一
  removeWeaklyReachableObjects()
  val key = UUID.randomUUID()
      .toString()
  // 记下观测开始的时间
  val watchUptimeMillis = clock.uptimeMillis()
  // 这里创建了一个自定义的弱引用,且调用了基类的WeakReference<Any>(referent, referenceQueue)构造器
  // 这样的话,弱引用被回收之前会出现在ReferenceQueue中
  val reference =
    KeyedWeakReference(watchedObject, key, name, watchUptimeMillis, queue)
  SharkLog.d {
      "Watching " +
          (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
          (if (name.isNotEmpty()) " named $name" else "") +
          " with key $key"
  }


  // 将key-reference保存到map中
  watchedObjects[key] = reference
  // 主线程5秒之后执行moveToRetained(key)方法
  checkRetainedExecutor.execute {
    moveToRetained(key)
  }
}

上面这段代码便是LeakCanary的关键代码之一:1. 将要观测的对象使用WeakReference保存起来,并在构造时传入一个ReferenceQueue,这样待观测的对象在被回收之前,会出现在ReferenceQueue中。2. 5秒钟之后再检查一下是否出现在了引用队列中,若出现了,则没有泄露。

为什么会是5S,这里猜测与Android GC有关。在Activity.H中,收到GC_WHEN_IDLE消息时会进行Looper.myQueue().addIdleHandler(mGcIdler),而mGcIdler最后会触发doGcIfNeeded操作,在该方法中会判断上次GC与现在时间的差值,而这个值就是MIN_TIME_BETWEEN_GCS = 5*1000

回到上面的代码,需要了解两个方法removeWeaklyReachableObjects()moveToRetained(key)。前者比较简单,就会将引用队列中出现的对象从map中移除,因为它们没有发生内存泄漏。但是注意一下注释,这里强调了一点:弱引用入队列发生在终结函数或者GC发生之前


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

然后我们接着看重头戏moveToRetained方法:


 
@Synchronized private fun moveToRetained(key: String) {
  removeWeaklyReachableObjects()
  val retainedRef = watchedObjects[key]
  if (retainedRef != null) {
    retainedRef.retainedUptimeMillis = clock.uptimeMillis()
    onObjectRetainedListeners.forEach { it.onObjectRetained() }
  }
}

5秒钟到了,还是先将引用队列中出现的对象从map中移除,因为它们没有内存泄漏。然后判断key还在不在map中,如果在的话,说明可能发生了内存泄漏。此时记下内存泄漏发生的时间,即更新retainedUptimeMillis字段,然后通知所有的对象,内存泄漏发生了。

我们回忆一下,此处的onObjectRetainedListeners只有一个,就是我们在Activity、Fragment的观测者安装完毕后,通知了InternalLeakCanary,而InternalLeakCanary添加了一个监听器,就是它自己。所以我们看看InternalLeakCanary.onObjectRetained()方法:

leakcanary-android-core/src/main/java/leakcanary/internal/InternalLeakCanary.kt


 
override fun onObjectRetained() {
  if (this::heapDumpTrigger.isInitialized) {
    heapDumpTrigger.onObjectRetained()
  }
}

跟踪一下HeapDumpTrigger.onObjectRetained()方法:

leakcanary-android-core/src/main/java/leakcanary/internal/HeapDumpTrigger.kt


 
fun 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 {
    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) {
    gcTrigger.runGc()
    retainedReferenceCount = objectWatcher.retainedObjectCount
  }


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


  if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
    showRetainedCountWithDebuggerAttached(retainedReferenceCount)
    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
  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)
    showRetainedCountWithHeapDumpFailed(retainedReferenceCount)
    return
  }
  lastDisplayedRetainedObjectCount = 0
  objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)


  HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}

上面的代码就是对于内存泄漏判定的代码了,首先进入onObjectRetained方法,该方法会调用scheduleRetainedObjectCheck方法。此方法也就是在后台线程中执行checkRetainedObjects方法来检查泄漏的对象:1. 首先获取泄漏对象的个数,如果大于0,则GC一次之后再次获取 2. 如果此时泄漏对象的个数大于等于5个config.retainedVisibleThreshold,则继续执行下面的代码,准备dump heap 3. 如果config里面配置的“调试时不允许dump heap”为false(默认值)且正在调试,则20s之后再试 4. 否则可以开始dump heap:此时会先记下dump发生的时间,取消内存泄漏通知,dump heap,清除所有观测事件小于等于dump发生时间的对象(因为这些对象已经处理完毕了),最后运行HeapAnalyzerService开始分析heap。

第26行的代码是如何获取泄露对象的个数的呢?我们想一下,在前面的代码中,主线程5秒之后执行了一段检测的代码,在这里面将所有泄露的对象都记下了当时的时间,存在retainedUptimeMillis字段里面。那么我们遍历所有元素,统计一下该字段不为默认值(-1)的个数即可:

leakcanary-object-watcher/src/main/java/leakcanary/ObjectWatcher.kt


 
/**
  * Returns the number of retained objects, ie the number of watched objects that aren't weakly
  * reachable, and have been watched for long enough to be considered retained.
  */
val retainedObjectCount: Int
  @Synchronized get() {
    removeWeaklyReachableObjects()
    return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
  }

第29行,如果有内存泄漏的话,会调用gcTrigger.runGc()方法,这里的gcTrigger我们提到过,是GcTrigger.Default

leakcanary/leakcanary-object-watcher/src/main/java/leakcanary/GcTrigger.kt


 
/**
  * Default implementation of [GcTrigger].
  */
object Default : GcTrigger {
  override fun runGc() {
    // Code taken from AOSP FinalizationTest:
    // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
    // java/lang/ref/FinalizationTester.java
    // System.gc() does not garbage collect every time. Runtime.gc() is
    // more likely to perform a gc.
    Runtime.getRuntime()
        .gc()
    enqueueReferences()
    System.runFinalization()
  }


  private fun enqueueReferences() {
    // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
    // references to the appropriate queues.
    try {
      Thread.sleep(100)
    } catch (e: InterruptedException) {
      throw AssertionError()
    }
  }
}

在上面注释中提到,System.gc()并不会每次都会执行GC,Runtime.gc()更有可能执行GC。
执行一次GC操作之后,下面粗暴的等待100ms,这样有足够的时间可以让弱引用移动到合适的引用队列里面。这就是GcTrigger.Default所干的事情。

GCTrigger触发GC之后,再次判断一下发生内存泄漏的对象的个数,如果仍然还有,那么肯定是泄漏无疑了,实锤!!
随后调用checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)方法,判断泄漏对象的个数是否达到了阈值,如果达到了则直接dump heap;否则发出一个内存泄漏的通知。
我们看一下这个方法:

private fun checkRetainedCount(
  retainedKeysCount: Int,
  retainedVisibleThreshold: Int
): Boolean {
  // lastDisplayedRetainedObjectCount默认值为0,此处我们肯定有内存泄漏,因此countChanged为true
  val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
  // 保存下当前的内存泄漏对象的个数
  lastDisplayedRetainedObjectCount = retainedKeysCount
  // 如果内存泄漏个数为0,则说明已经处理了所有的内存泄漏
  if (retainedKeysCount == 0) {
    SharkLog.d { "No retained objects" }
    if (countChanged) {
      showNoMoreRetainedObjectNotification()
    }
    return true
  }


  // 如果泄漏个数小于5个
  if (retainedKeysCount < retainedVisibleThreshold) {
    if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
      SharkLog.d {
          "Found $retainedKeysCount retained objects, which is less than the visible threshold of $retainedVisibleThreshold"
      }
      // 展示一个内存泄漏发生的通知
      showRetainedCountBelowThresholdNotification(retainedKeysCount, retainedVisibleThreshold)
      // 2秒钟之后再次执行检查泄漏对象的方法,看看泄漏个数是否有变化
      scheduleRetainedObjectCheck(
          "Showing retained objects notification", WAIT_FOR_OBJECT_THRESHOLD_MILLIS
      )
      return true
    }
  }
  // 如果泄漏个数大于等于5个,返回false,则返回后checkRetainedObjects方法会继续执行
  // 此时就会dump heap
  return false
}
至此,我们已经知道了内存泄漏是如何判定的,正如第3节开头所述:

LeakCanary 2.0 beta 3 检测内存泄漏原理
在Activity destroy后将Activity的弱引用关联到ReferenceQueue中,这样Activity将要被GC前,会出现在ReferenceQueue中。
随后,会向主线程的抛出一个5秒后执行的Runnable,用于检测内存泄漏。
这段代码首先会将引用队列中出现的对象从观察对象数组中移除,然后再判断要观察的此对象是否存在。若不存在,则说明没有内存泄漏,结束。否则,就说明可能出现了内存泄漏,会调用Runtime.getRuntime().gc()进行GC,等待100ms后再次根据引用队列判断,若仍然出现在引用队列中,那么说明有内存泄漏,此时根据内存泄漏的个数弹出通知或者开始dump hprof。

LeakCanary2往后就是如何生成hprof文件以及如何解析了

以上是关于浅谈LeakCanary2源码的主要内容,如果未能解决你的问题,请参考以下文章

十年Java”老兵“浅谈源码的七大设计模式

浅谈Netty中ServerBootstrap服务端源码(含bind全流程)

浅谈Mybatis

浅谈责任链设计模式在框架源码中的运用

Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段

浅谈AngularJS中的$parse和$eval