文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

内存泄漏检测工具LeakCanary源码解析

2023-01-28 06:02

关注

前言

LeakCanary是一个简单方便的内存泄漏检测工具,它是由大名鼎鼎的Square公司出品并开源的出来的。目前大部分APP在开发阶段都会接入此工具用来检测内存泄漏问题。它让我们开发者可以在开发阶段就发现一些没有注意到或者不规范的代码导致的内存泄漏,从而就避免了因内存泄漏而最终导致OOM的问题。

使用

LeakCanary的使用非常简单,我们只需要在项目module下的build.gradle文件里dependencies里面加上依赖就行。

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

只需要加上依赖就行了,它会在应用启动的时候自动进行初始化。并且由于我们使用的是debugImplementation,因此它只在debug包下有效,所以不用担心这个会在release包下造成性能影响。

源码解析

接下来将通过阅读源码,并从以下几个方面去深入Leakcanary原理,本文将以2.9.1源码为例。

LeakCanary自动初始化

我们集成的时候只需要添加依赖,不需要像其他第三方SDK那样在Application或其他地方手动调用初始化方法,其实它是借助ContentProvider来实现的,通过源码来看一下MainProcessAppWatcherInstaller这个类:

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

如果对app的启动流程有了解的童鞋就清楚,APP启动时,ContentProviderApplication的顺序是:

Application.attachBaseContext() -> contentProvider.onCreate() -> Application.onCreate()

通过源码来看下,在ActivityThread类里面handleBindApplication下:

@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
    ...
    // 这边创建application,并会调用到application的attach()方法,最终调用到attachBaseContext()方法
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    ...
    // don't bring up providers in restricted mode; they may depend on the
    // app's custom Application class
    if (!data.restrictedBackupMode) {
        if (!ArrayUtils.isEmpty(data.providers)) {
            //初始化Provider并且调用了Provider的onCreate()方法
            installContentProviders(app, data.providers);
        }
    }
    ...
    try {
        //调用了Application的onCreate()方法
        mInstrumentation.callApplicationOnCreate(app);
    } catch (Exception e) {
        if (!mInstrumentation.onException(app, e)) {
            throw new RuntimeException(
              "Unable to create application " + app.getClass().getName()
              + ": " + e.toString(), e);
        }
    }
    ...
}

这样ContentProvider会在Application.onCreate()前初始化,就会调用到了LeakCanary的初始化方法,实现了自动初始化。

注意: 这样使用ContentProvider的进行初始化的写法虽然方便,方便了开发人员集成使用。但是可能会带来启动耗时的问题,并且无法控制初始化的时机。不过这对于LeakCanary这个工具库来说影响不大,因为这个也只在Debug阶段使用。

如何关闭自动初始化

如果我们想要关闭自动化初始化,自己选择在合适的地方进行初始化的话,可以通过覆盖资源文件里面的值来进行关闭

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <bool name="leak_canary_watcher_auto_install">false</bool>
</resources>

并且在合适的位置自行调用 AppWatcher.manualInstall(application)进行手动初始化。

LeakCanary初始化做了什么

上面我们说到LeakCanary是使用ContentProvider进行初始化的,那么我们就从MainProcessAppWatcherInstaller开始。

internal class MainProcessAppWatcherInstaller : ContentProvider() {
  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    //这边调用初始化的方法
    AppWatcher.manualInstall(application)
    return true
  }
  ...
 }

这边会在这个MainProcessAppWatcherInstalleronCreate方法里面调用AppWatcher.manualInstall(application),进行初始化。

@JvmOverloads
fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
  watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
  //先判断是否是在主线程,如果不是在主线程则抛出异常
  checkMainThread()
  //判断是否已经初始化了,如果已经初始化了则抛出异常,避免重复初始化
  if (isInstalled) {
    throw IllegalStateException(
      "AppWatcher already installed, see exception cause for prior install call", installCause
    )
  }
  //判断设置的延迟时间是否小于0,这个时间是用来延迟检测被观察的对象是否已被释放,
  //也就是说如果到了这个时间,对象仍没有被释放,那么可能出现了内存泄漏
  check(retainedDelayMillis >= 0) {
    "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
  }
  this.retainedDelayMillis = retainedDelayMillis
  if (application.isDebuggableBuild) {
    LogcatSharkLog.install()
  }
  //初始化 InternalLeakCanary,并调用了InternalLeakCanary.invoke()方法,
  //这个类是用来检查判断对象是否被回收
  LeakCanaryDelegate.loadLeakCanary(application)
  //开启监听器,也就是appDefaultWatchers(application)方法里面的
  watchersToInstall.forEach {
    it.install()
  }
  // Only install after we're fully done with init.
  installCause = RuntimeException("manualInstall() first called here")
}

manualInstall方法里面先做一些判断校验合法,并执行了LeakCanaryDelegate.loadLeakCanary,这里面会调用内部的invoke()方法,对LeakCanary的检查判断泄漏的一些类进行初始化。接下来会对watchersToInstall列表里面的四种观察类型的生命周期监视器调用install()方法,开启监听。

再来看下watchersToInstall,默认情况下使用的是 appDefaultWatchers(application)返回的list集合。

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

这边会创建ReachabilityWatcher,也就是objectWatcher,这边看下他的初始化:

object AppWatcher {
 ...

val objectWatcher = ObjectWatcher(
  clock = { SystemClock.uptimeMillis() },
  checkRetainedExecutor = {
    check(isInstalled) {
      "AppWatcher not installed"
    }
    mainHandler.postDelayed(it, retainedDelayMillis)
  },
  isEnabled = { true }
 )
 ...
}

这边重点看下checkRetainedExecutor这个入参,它是一个Executor,执行run时,就会调用mainHandler.postDelayed,后面在对对象回收检查时,会调用run方法,通过postDelayed延时去检查。

接着上面的appDefaultWatchers方法,里面会创建四种类型的生命周期监听器,分别是Activity、Fragment、RootView和Service。下面将对这4者的源码进行分析

ActivityWatcher

class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
  //回调,这个是向application.registerActivityLifecycleCallbacks注册activity生命周期回调
  //这边通过监听Activity的onDestroyed来观察它的回收状态
  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        //一旦activity进入到destory,则开始通过onActivityDestroyed观察它的回收状态
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }
  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }
  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
}

Activity的监听比较简单,只需要注册一个回调即可,因为Android本身有提供全局监听Activity的生命周期的回调。只需要在onActivityDestroyed回调里面对对象回收情况进行观察,因为一旦进入到onActivityDestroyed就表明Activity退出了,此时可以开始观察Activity是否回收。

这边有一个知识点,就是上面的Application.ActivityLifecycleCallbacks by noOpDelegate(),这边使用到了委托noOpDelegate(),这个的作用是使得接口类可以只实现自己想要的方法,而不需要全部实现。

FragmentAndViewModelWatcher

Fragment的监听就比较麻烦了,需要对不同包的Fragment做适配处理,分别是:

private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
  val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
  //添加android.app.Fragment监听,创建AndroidOFragmentDestroyWatcher,并加入到集合中
  if (SDK_INT >= O) {
    fragmentDestroyWatchers.add(
      AndroidOFragmentDestroyWatcher(reachabilityWatcher)
    )
  }
  //添加 androidx.fragment.app.Fragment监听,
  //通过反射的方式创建AndroidXFragmentDestroyWatcher,并加入到集合中
  getWatcherIfAvailable(
    ANDROIDX_FRAGMENT_CLASS_NAME,
    ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  }
  //添加android.support.v4.app.Fragment监听
  //通过反射的方式 创建 AndroidSupportFragmentDestroyWatcher,并加入到集合中
  getWatcherIfAvailable(
    ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
    ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  } 
  fragmentDestroyWatchers
}

这边会对不同包下的Fragment创建Watcher,并把它们加入到一个List中,接下来就是当外部调用install()时,就会调用到application.registerActivityLifecycleCallbacks(lifecycleCallbacks),下面来看下这个lifecycleCallbacks

private val lifecycleCallbacks =
  object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
    override fun onActivityCreated(
      activity: Activity,
      savedInstanceState: Bundle?
    ) {
      for (watcher in fragmentDestroyWatchers) {
        watcher(activity)
      }
    }
  }

可以看到,这边是在onActivityCreated的时候调用各个Watcher的invoke()方法。然后在invoke()方法里面通过activity.fragmentManageractivity.supportFragmentManager调用registerFragmentLifecycleCallbacks去注册Fragment的生命周期回调。

这边就只看下AndroidX下的Fragment生命周期监听

internal class AndroidXFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }
    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) {
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
}

AndroidOFragmentDestroyWatcher类里面,会创建一个Fragment的生命周期回调,也就是FragmentManager.FragmentLifecycleCallbacks,并在onFragmentViewDestroyedonFragmentDestroyed这两个回调方法里面进行对象回收检查。

在该类里面,我们还能看到install了ViewModel,对ViewModel的生命周期进行观察。这边会区分是Activity的ViewModel还是Fragment的ViewModel。

那么在ViewModelClearedWatcher里面,又是怎么对viewModel的生命周期进行观察的呢?这边就不贴代码了:

RootViewWatcher

private val listener = OnRootViewAddedListener { rootView ->
  val trackDetached = when(rootView.windowType) {
    PHONE_WINDOW -> {
      when (rootView.phoneWindow?.callback?.wrappedCallback) {
        // Activities are already tracked by ActivityWatcher
        // activity不需要重复注册
        is Activity -> false
        //监听dialog,这边还需要判断下资源文件里面的配置信息是否是打开的
        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, 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) {
        //当view从界面上移除时时,就执行监听,通过Handler.post()
        mainHandler.post(watchDetachedView)
      }
    })
  }
}
override fun install() {
  //使用三方库的Curtains来对view进行状态的监听
  Curtains.onRootViewsChangedListeners += listener
}

RootView这边只要监听一些dialog、tooltip、toast等,代码不长,简单说下

ServiceWatcher


//存放即将stop的service
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }
//通过反射的方式调用ActivityThread里面的currentActivityThread方法,也就是取到ActivityThread对象
private val activityThreadInstance by lazy {
  activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
}
//通过反射方式从ActivityThread取出mServices
private val activityThreadServices by lazy {
  val mServicesField =
    activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }
  @Suppress("UNCHECKED_CAST")
  mServicesField[activityThreadInstance] as Map<IBinder, Service>
}
override fun install() {
  //做一些检查
  checkMainThread()
  check(uninstallActivityThreadHandlerCallback == null) {
    "ServiceWatcher already installed"
  }
  check(uninstallActivityManager == null) {
    "ServiceWatcher already installed"
  }
  try {
    //hook ActivityThread里面的mH,也就是Handler,并替换里面的mCallBack
    swapActivityThreadHandlerCallback { mCallback ->
      //还原hook的mCallback,uninstall()时使用
      uninstallActivityThreadHandlerCallback = {
        swapActivityThreadHandlerCallback {
          mCallback
        }
      }
      //返回需要替换的mCallback,在这里面监听handler消息,
      Handler.Callback { msg ->
        // https://github.com/square/leakcanary/issues/2114
        // On some Motorola devices (Moto E5 and G6), the msg.obj returns an ActivityClientRecord
        // instead of an IBinder. This crashes on a ClassCastException. Adding a type check
        // here to prevent the crash.
        if (msg.obj !is IBinder) {
          return@Callback false
        }
        //如果收到了Service的Stop的消息,表示service要结束了,
        //此时调用onServicePreDestroy,将当前service加入到servicesToBeDestroyed集合里面去
        if (msg.what == STOP_SERVICE) {
          val key = msg.obj as IBinder
          activityThreadServices[key]?.let {
            onServicePreDestroy(key, it)
          }
        }
        //继续处理消息
        mCallback?.handleMessage(msg) ?: false
      }
    }
    //这边hook ActivityManagerService
    swapActivityManager { activityManagerInterface, activityManagerInstance ->
      uninstallActivityManager = {
        swapActivityManager { _, _ ->
          activityManagerInstance
        }
      }
      //使用动态代理的方式
      Proxy.newProxyInstance(
        activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
      ) { _, method, args ->
        //如果执行serviceDoneExecuting方法时,则调用onServiceDestroyed()
        //方法来观察service的内存回收情况
        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" }
  }
}

Service的结束监听是通过hool的方式进行的,步骤如下:

Leakcanary对象泄漏检查

在被观察类型的生命周期的结束时,会调用到reachabilityWatcher.expectWeaklyReachable这个方法

我们跟进去,并找到实现类,来到ObjectWatcher里面,找到expectWeaklyReachable方法。

ObjectWatcher:
@Synchronized override fun expectWeaklyReachable(
  watchedObject: Any,
  description: String
) {
  //这边在前面初始化的时候默认传进来是true,因此会继续往下执行下面的代码
  if (!isEnabled()) {
    return
  }
  //这边会先先处理下,将被回收的对象从watchedObjects这个待观察的集合里面移除
  removeWeaklyReachableObjects()
  //创建一个随机的UUID,用来当做待观察对象的key,方便从watchedObjects这个map取值
  val key = UUID.randomUUID()
    .toString()
  // 获取当前的时间,自系统开机到现在的一个时间
  val watchUptimeMillis = clock.uptimeMillis()
  //创建弱引用对象,也就是WeakReference
  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)
  }
}

在这个方法里面会先执行一次removeWeaklyReachableObjects(),将已被回收的对象从待观察的集合watchedObjects里面移除,然后创建弱引用对象,接着开启延时检查,默认等待5秒,使用的是mainHandler.postDelayed(it, retainedDelayMillis)去做延时的。

先看下removeWeaklyReachableObjects()这个方法:

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?
  //通过迭代遍历的方式,判断queue里面时候有被回收的对象
  do {
    ref = queue.poll() as KeyedWeakReference?
    if (ref != null) {
      //如果对象被回收了 ,则讲弱引用从待观察的map里面移除
      watchedObjects.remove(ref.key)
    }
  } while (ref != null)
}

这个方法里面代码很少,这里面做的工作就是判断queue里面有没有被回收的对象,如果有则将改对象的弱引用从待观察的集合里面移除。

然后在来看下延时处理里面做了什么事:

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

可以看到里面一开始又调用了一次removeWeaklyReachableObjects()方法。这边是第二次调用了,尝试移除这5秒内已经被回收的对象。如果此时对象仍没有被回收,也就是还在待观察集合里面,那么就开始进入到回调里面去,也就是OnObjectRetainedListener.onObjectRetained()里面去,这边这个onObjectRetainedListeners列表里面目前就只有一个, 它是在InternalLeakCanaryinvoke里面调用的。

AppWatcher
  -> manualIstall()
    -> LeakCanaryDelegate.loadLeakCanary(application)
      -> InternalLeakCanary
       -> invoke()
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    ...
    override fun invoke(application: Application) {
      _application = application
      checkRunningInDebuggableBuild()
      //添加监听,因为InternalLeakCanary实现了OnObjectRetainedListener接口,因此直接传this
      AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
      ...
    }
    ...
}

因此最终是回调到InternalLeakCanaryonObjectRetained()方法里面,然后在调用到scheduleRetainedObjectCheck()。最终进入到heapDumpTrigger.scheduleRetainedObjectCheck()方法里面

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    ...
    //回调到这个方法,然后又调用了scheduleRetainedObjectCheck
    override fun onObjectRetained() = scheduleRetainedObjectCheck()
    fun scheduleRetainedObjectCheck() {
      //先判断heapDumpTrigger是否有初始化了,
      if (this::heapDumpTrigger.isInitialized) {
        heapDumpTrigger.scheduleRetainedObjectCheck()
      }
    }
    ...   
}

接下来在进入到heapDumpTrigger.scheduleRetainedObjectCheck()方法里面

fun scheduleRetainedObjectCheck(
  delayMillis: Long = 0L
) {
  val checkCurrentlyScheduledAt = checkScheduledAt
  //如果之前的子线程任务来没开始执行则返回,也就是就是下面的backgroundHandler.postDelayed
  if (checkCurrentlyScheduledAt > 0) {
    return
  }
  checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
  //让子线程延时去delayMillis执行,通过上面的调用链进入此方法则delayMillis为0,因此会马上执行
  backgroundHandler.postDelayed({
    //子线程任务开始执行,就将checkScheduledAt设置为0,以便下一次出现内存泄漏时能还能进来
    checkScheduledAt = 0
    checkRetainedObjects()
  }, delayMillis)
}

接下来在进入到checkRetainedObjects()这个方法里面去。由于上面是通过子线程Handler去post,因此该方法是运行在子线程里面

private fun checkRetainedObjects() {
  ...
  //看下此时待观察的集合里面还有多少个没有被分析的弱引用对象
  //也就是retainedUptimeMillis != -1L 的对象的个数
  //并且在调用retainedObjectCount.get()的时候,还会再调用一次removeWeaklyReachableObjects(),尝试再次移除一遍
  var retainedReferenceCount = objectWatcher.retainedObjectCount
  //如果此时还有泄漏对象,则调用gc,并重新再获取一次数量
  if (retainedReferenceCount > 0) {
    gcTrigger.runGc()
    retainedReferenceCount = objectWatcher.retainedObjectCount
  }
  //这边判断应用是出于前台还是后台,
  //如果是在前台,则个数达到5个时才dump,如果应用在后台则只要有一个都会进行dump
  if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  //如果这个时间与上次dump的时间过短,则会重新调用scheduleRetainedObjectCheck,
  //并延迟执行,这个延迟的时间 = 60s - 时间差,避免两次dump的时间过短,影响使用
  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
  dumpHeap(
    retainedReferenceCount = retainedReferenceCount,
    retry = true,
    reason = "$retainedReferenceCount retained objects, app is $visibility"
  )
}

在这个checkRetainedObjects()方法里面执行的步骤:

接下来来看下dumpHeap这个方法,这边就不分析dump的源码了,主要看下dump后执行操作。

private fun dumpHeap(
  retainedReferenceCount: Int,
  retry: Boolean,
  reason: String
) {
    ...
    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    ...
    //执行dump,并计算dump的时间
    durationMillis = measureDurationMillis {
      //执行dump,这边就不分析了,感兴趣可以自行点进去看下
      configProvider().heapDumper.dumpHeap(heapDumpFile)
    }
    ...
    //dump之后处理以分析的对象
    objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
}

这边注意下:objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)这个方法,上面我们说到当对象被通过dump分析之后,会将retainedUptimeMillis这个时间改为-1。现在我们来看下这个方法里面的源码:


@Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
  //从待观察集合里面过滤出时间小于等于当前dump时间的弱引用
  val weakRefsToRemove =
    watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }
  //遍历执行clear,将时间设置为-1,这个在下次触发执行checkRetainedObjects,里面不会重复判断
  weakRefsToRemove.values.forEach { it.clear() }
  //然后从待观察列表里面移除
  watchedObjects.keys.removeAll(weakRefsToRemove.keys)
}

这个方法里面会过滤出已经被dump过的弱引用对象,因为当它的watchUptimeMillis <= heapDumpUptimeMillis时,dump会将内存中当前时间点之前的疑似泄漏的对象都列举出来,所以只要是在当前dump时间点之前加入的都可以认为是已经dump了。

然后接下调用过滤出来的弱引用对象的claer()方法,将watchUptimeMillis时间设置为-1,并且从待观察集合里面移除。

总结

经过了上面的源码分析,接下来来总结下:

以上就是LeakCanary 2.9.1的源码解析,更多关于内存泄漏检测LeakCanary的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯