LeakCanary 2.7 原理分析

1. LeakCanary 初始化

我们直接引入依赖即可,无需代码初始化,LeakCanary 会在 app 初始化时自动初始化自身。

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
	Application->attachBaseContext 
=====>
	ContentProvider->onCreate 
=====>
	Application->onCreate 
=====>
	Activity->onCreate 
/**
 * Content providers are loaded before the application class is created. [AppWatcherInstaller] is
 * used to install [leakcanary.AppWatcher] on application start.
 */
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)
  ) {
    checkMainThread()
    ..

	//此处 watchersToInstall 可根据需要自定义 --> eg:
		 //watchedObject !is BadSdkLeakingFragment
		 //it !is RootViewWatcher
    watchersToInstall.forEach {
      it.install()
    }
  }

manualInstall 方法的参数 watchersToInstall 默认实现 ActivityFragmentViewViewModelService 的监听:

fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
      ActivityWatcher(application, reachabilityWatcher),
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      RootViewWatcher(reachabilityWatcher),
      ServiceWatcher(reachabilityWatcher)
    )
  }

2. Watcher

2.1 ActivityWatcher

/**
 * Expects activities to become weakly reachable soon after they receive the [Activity.onDestroy]
 * callback.
 */
class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

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

  //LeakCanary init时调用
  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

  //没找到调用此方法的地方
  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
}

通过 application 监听 Activity 的生命周期,在 onDestory 回调reachabilityWatcher.expectWeaklyReachable 方法,做内存泄漏的检查工作。

2.2 FragmentAndViewModelWatcher

FragmentAndViewModelWatcher 主要检测了三种

  • Fragments (Support Library, Android X and AOSP)
    • Fragment#onDestroy()
  • Fragment views (Support Library, Android X and AOSP)
    • Fragment#onDestroyView()
  • Android X view models (both activity and fragment view models)
    • ViewModel#onCleared()
/**
 * Expects:
 * - Fragments (Support Library, Android X and AOSP) to become weakly reachable soon after they
 * receive the Fragment#onDestroy() callback.
 * - Fragment views (Support Library, Android X and AOSP) to become weakly reachable soon after
 * fragments receive the Fragment#onDestroyView() callback.
 * - Android X view models (both activity and fragment view models) to become weakly reachable soon
 * after they received the ViewModel#onCleared() callback.
 */
class FragmentAndViewModelWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
        AndroidOFragmentDestroyWatcher(reachabilityWatcher)
      )
    }

    getWatcherIfAvailable(
      ANDROIDX_FRAGMENT_CLASS_NAME,
      ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      fragmentDestroyWatchers.add(it)
    }

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

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

  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
  ...
  • AndroidOFragmentDestroyWatcher
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)
  }

2.3 RootViewWatcher

Expects root views to become weakly reachable soon after they are removed from the window

通过 OnAttachStateChangeListeneronViewAttachedToWindowonViewDetachedFromWindow 方法回调可做内存泄漏的检查工作:

/**
 * Expects root views to become weakly reachable soon after they are removed from the window
 * manager.
 */
class RootViewWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val listener = OnRootViewAddedListener { rootView ->
    val trackDetached = when(rootView.windowType) {
      PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
          // Activities are already tracked by ActivityWatcher
          is Activity -> false
          is Dialog -> rootView.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, UNKNOWN -> true
    }
    if (trackDetached) {
      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
  }
}

2.4 ServiceWatcher

Service 通过hook ActivityThread的 H 类和 AMS,当 AMS调用 serviceDoneExecuting 方法可做内存泄漏的检查工作。

/**
 * Expects services to become weakly reachable soon after they receive the [Service.onDestroy]
 * callback.
 */
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {

  private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()

  ...
  
  override fun install() {
    checkMainThread()
    
    try {
      //hook ActivityThread中H类的Callback 
      swapActivityThreadHandlerCallback { mCallback ->
        ..
        Handler.Callback { msg ->
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            //在收到STOP_SERVICE时将Service以弱应用形式记录
            activityThreadServices[key]?.let {
              onServicePreDestroy(key, it)
            }
          }
          mCallback?.handleMessage(msg) ?: false
        }
      }
	  //这里hook了AMS
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        ...
        //通过代理形式 在调用serviceDoneExecuting方法时可判断对象是否泄漏
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          //serviceDoneExecuting is METHOD_SERVICE_DONE_EXECUTING 
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              onServiceDestroyed(token)
            }
          }
          ...
          //method.invoke(activityManagerInstance)
        }
      }
    } catch (ignored: Throwable) {
      SharkLog.d(ignored) { "Could not watch destroyed services" }
    }
  }


  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 代码

  companion object {
    private const val STOP_SERVICE = 116

    private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
  }

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

2.5 OnObjectRetainedListener

Listener used by [ObjectWatcher] to report retained objects.

/**
 * Listener used by [ObjectWatcher] to report retained objects.
 */
fun interface OnObjectRetainedListener {

  /**
   * A watched object became retained.
   */
  fun onObjectRetained()

  companion object {
   
    inline operator fun invoke(crossinline block: () -> Unit): OnObjectRetainedListener =
      object : OnObjectRetainedListener {
        override fun onObjectRetained() {
          block()
        }
      }
  }
}
  • InternalLeakCanary#invoke

此回调中会初始化一些检测内存泄露过程中需要的对象:

override fun invoke(application: Application) {
    _application = application

    checkRunningInDebuggableBuild()

    AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

    val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))

    val gcTrigger = GcTrigger.Default// 配置项

    val configProvider = { LeakCanary.config }

	// 发起内存泄漏检测的线程
    val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
    handlerThread.start()
    val backgroundHandler = Handler(handlerThread.looper)

    heapDumpTrigger = HeapDumpTrigger(
      application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
      configProvider
    )
    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    registerResumedActivityListener(application)
    addDynamicShortcut(application)

    // We post so that the log happens after Application.onCreate()
    mainHandler.post {
      // https://github.com/square/leakcanary/issues/1981
      // We post to a background handler because HeapDumpControl.iCanHasHeap() checks a shared pref
      // which blocks until loaded and that creates a StrictMode violation.
      backgroundHandler.post {
        SharkLog.d {...}
      }
    }
  }
  • heapDumper 用于确认内存泄漏之后进行 heap dump 工作。
  • gcTrigger 用于发现可能的内存泄漏之后手动调用 GC 确认是否真的为内存泄露。

这两个对象是 LeakCanary 检测内存泄漏的核心

2.6 ObjectWatcher

/**
 * [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances
 * that reference watches objects, and check if those references have been cleared as expected on
 * the [checkRetainedExecutor] executor. If not, these objects are considered retained and
 * [ObjectWatcher] will then notify registered [OnObjectRetainedListener]s on that executor thread.
 *
 * [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant
 * delay to give the GC the opportunity to identify weakly reachable objects.
 *
 * [ObjectWatcher] is thread safe.
 */
// Thread safe by locking on all methods, which is reasonably efficient given how often
// these methods are accessed.
class ObjectWatcher constructor(
  private val clock: Clock,
  private val checkRetainedExecutor: Executor,
  /**
   * Calls to [watch] will be ignored when [isEnabled] returns false
   */
  private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {

  private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()

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

  private val queue = ReferenceQueue<Any>()

  /**
   * Returns true if there are watched objects that aren't weakly reachable, and
   * have been watched for long enough to be considered retained.
   */
  val hasRetainedObjects: Boolean
    @Synchronized get() {
      removeWeaklyReachableObjects()
      return watchedObjects.any { it.value.retainedUptimeMillis != -1L }
    }

  /**
   * 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 }
    }

  /**
   * Returns true if there are watched objects that aren't weakly reachable, even
   * if they haven't been watched for long enough to be considered retained.
   */
  val hasWatchedObjects: Boolean
    @Synchronized get() {
      removeWeaklyReachableObjects()
      return watchedObjects.isNotEmpty()
    }

  /**
   * Returns the objects that are currently considered retained. Useful for logging purposes.
   * Be careful with those objects and release them ASAP as you may creating longer lived leaks
   * then the one that are already there.
   */
  val retainedObjects: List<Any>
    @Synchronized get() {
      removeWeaklyReachableObjects()
      val instances = mutableListOf<Any>()
      for (weakReference in watchedObjects.values) {
        if (weakReference.retainedUptimeMillis != -1L) {
          val instance = weakReference.get()
          if (instance != null) {
            instances.add(instance)
          }
        }
      }
      return instances
    }

  @Synchronized fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) {
    onObjectRetainedListeners.add(listener)
  }

  @Synchronized fun removeOnObjectRetainedListener(listener: OnObjectRetainedListener) {
    onObjectRetainedListeners.remove(listener)
  }

  /**
   * Identical to [watch] with an empty string reference name.
   */
  @Deprecated(
    "Add description parameter explaining why an object is watched to help understand leak traces.",
    replaceWith = ReplaceWith(
      "expectWeaklyReachable(watchedObject, \"Explain why this object should be garbage collected soon\")"
    )
  )
  fun watch(watchedObject: Any) {
    expectWeaklyReachable(watchedObject, "")
  }

  @Deprecated(
    "Method renamed expectWeaklyReachable() to clarify usage.",
    replaceWith = ReplaceWith(
      "expectWeaklyReachable(watchedObject, description)"
    )
  )
  fun watch(
    watchedObject: Any,
    description: String
  ) {
    expectWeaklyReachable(watchedObject, description)
  }


  @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
    removeWeaklyReachableObjects()
    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
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

  /**
   * Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on
   * [clock] [Clock.uptimeMillis])
   */
  @Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
    val weakRefsToRemove =
      watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }
    weakRefsToRemove.values.forEach { it.clear() }
    watchedObjects.keys.removeAll(weakRefsToRemove.keys)
  }

  /**
   * Clears all [KeyedWeakReference]
   */
  @Synchronized fun clearWatchedObjects() {
    watchedObjects.values.forEach { it.clear() }
    watchedObjects.clear()
  }

  @Synchronized private fun moveToRetained(key: String) {
    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)
  }
}

3. 寻找泄露对象-ObjectWatcher

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

3.1 检测内存泄漏的时机


  • ActivityWatcher
    • Activity#onActivityDestroyed

  • FragmentAndViewModelWatcher
    • Fragments (Support Library, Android X and AOSP)
      • Fragment#onDestroy()
    • Fragment views (Support Library, Android X and AOSP)
      • Fragment#onDestroyView()
    • Android X view models (both activity and fragment view models)
      • ViewModel#onCleared()

Expects root views to become weakly reachable soon after they are removed from the window

  • RootViewWatcher
    • rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow

  • ServiceWatcher
    • AMS#serviceDoneExecuting

3.2 需要被检测是否内存泄露的对象 – WeakReferences 何时添加至监听队列?

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.

//主要看上面描述就够了
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)
  }

3.3 寻找可能的泄漏对象

 @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    ...
    //移除非泄漏的弱引用对象
    removeWeaklyReachableObjects()
    //为此对象生成唯一随机id
    val key = UUID.randomUUID()
      .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    ...

	//将此对象包成weakRefrence对象 然后加入队列中
    watchedObjects[key] = reference
    //5s后弱引用依然存在则有泄漏风险
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

3.4 检测泄漏的对象

  • HeapDumpTrigger
private fun checkRetainedObjects() {
	...
    var retainedReferenceCount = objectWatcher.retainedObjectCount
	
	//存在泄漏对象的话手动GC一次 然后再次检测
    if (retainedReferenceCount > 0) {
      gcTrigger.runGc()
      //GC 后重新计算 retainedReferenceCount 
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }

	//如果泄漏数量不超出阈值(默认5) 结束
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

	//泄漏数量 > 阈值(默认5),存在内存泄漏,生成hprof文件
    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    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"
    //dump文件 通过原生Debug.dumpHprofData(heapDumpFile.absolutePath);
    dumpHeap(
      retainedReferenceCount = retainedReferenceCount,
      retry = true,
      reason = "$retainedReferenceCount retained objects, app is $visibility"
    )
  }
  • retainedReferenceCount
/**
   * 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 }
    }
  • 这一步并不会立刻触发 dump 生成 hprof 文件分析,而是会先触发GC回收对象,再次判断未回收的对象数量
  • 如果超出5个,通过原生方式生成hprof文件,再通过HeapAnalyzerService分析这个文件。

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

4. 分析 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>

  ...
}

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

5. 总结

5.1 内存泄露的观察对象

不再需要的对象依然被引用,导致对象被分配的内存无法被回收 --> GC 回收机制

默认情况下 LeakCanary 会观察以下对象:

  • Activity
  • Fragment
  • RootView
  • Service

  • ActivityWatcher
    • Activity#onActivityDestroyed
  • FragmentAndViewModelWatcher
    • Fragments (Support Library, Android X and AOSP)
      • Fragment#onDestroy()
    • Fragment views (Support Library, Android X and AOSP)
      • Fragment#onDestroyView()
    • Android X view models (both activity and fragment view models)
      • ViewModel#onCleared()
  • RootViewWatcher
    • rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow
  • ServiceWatcher
    • AMS#serviceDoneExecuting

5.2 如何触发检测

  • Activity 举例
    • app 运行时会 registerActivityLifecycleCallbacks
    • Activity onDestory 调用 reachabilityWatcher.expectWeaklyReachable 去检测内存泄漏
private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

ActivityWatcher

fun interface ReachabilityWatcher {

  /**
   * Expects the provided [watchedObject] to become weakly reachable soon. If not,
   * [watchedObject] will be considered retained.
   */
  fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  )
}
  • ObjectWatcher#expectWeaklyReachable
 @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
    removeWeaklyReachableObjects()
    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
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

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

可以看出

  1. 传入的观察对象都会被存储在watchedObjects中
  2. 会为每个watchedObject生成一个KeyedWeakReference弱引用对象并与一个queue关联,当对象被回收时,该弱引用对象将进入queue当中
  3. 在检测过程中,我们会调用多次removeWeaklyReachableObjects,将已回收对象从watchedObjects中移除
  4. 如果watchedObjects中没有移除对象,证明它没有被回收,那么就会调用moveToRetained

5.3 checkRetainedInstances() 方法去确认泄露

  • ObjectWatcher#moveToRetained
@Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }
  • OnObjectRetainedListener#onObjectRetained
/**
 * Listener used by [ObjectWatcher] to report retained objects.
 *
 * This is a functional interface with which you can create a [OnObjectRetainedListener] from a lambda.
 */
fun interface OnObjectRetainedListener {

  /**
   * A watched object became retained.
   */
  fun onObjectRetained()
  ...
}
  • InternalLeakCanary#
override fun onObjectRetained() = scheduleRetainedObjectCheck()

  fun scheduleRetainedObjectCheck() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.scheduleRetainedObjectCheck()
    }
  }
  • HeapDumpTrigger#scheduleRetainedObjectCheck
fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    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

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

        val nopeReason = iCanHasHeap.reason()
        val wouldDump = !checkRetainedCount(
          retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
        )

        if (wouldDump) {
          val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
          onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
          showRetainedCountNotification(
            objectCount = retainedReferenceCount,
            contentText = uppercaseReason
          )
        }
      }
      ...
      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
    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(
      retainedReferenceCount = retainedReferenceCount,
      retry = true,
      reason = "$retainedReferenceCount retained objects, app is $visibility"
    )
  }

  1. 如果retainedObjectCount数量大于0,则进行一次GC,避免额外的Dump
  2. 默认情况下,如果retainedReferenceCount<5,不会进行Dump,节省资源
  3. 如果两次Dump之间时间少于60s,也会直接返回,避免频繁Dump
  4. 调用heapDumper.dumpHeap()进行真正的Dump操作
  5. Dump之后,要删除已经处理过了的引用
  6. 调用HeapAnalyzerService.runAnalysis对结果进行分析

对象应该被销毁时观察,eg: Activity onDestory() 时添加至 WeakRefrence 观察队列(参考3.3),五秒后该对象还在观察队列中代表可能存在内存泄漏

在这里插入图片描述

5.4 为什么不能用于线上

  1. 每次内存泄漏以后,都会生成一个.hprof文件,然后解析,并将结果写入.hprof.result。增加手机负担,引起手机卡顿等问题。
  2. 多次调用GC,可能会对线上性能产生影响
  3. 同样的泄漏问题,会重复生成 .hprof 文件,重复分析并写入磁盘。
  4. hprof文件较大,信息回捞成问题。

6. 参考链接

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值