美文网首页
LeakCanary浅析

LeakCanary浅析

作者: baifanger | 来源:发表于2020-10-28 18:11 被阅读0次

前提:LeakCanary 版本v2.4; Android 8.0
    LeakCanary相信很多开发者都用过,也是目前为止我看到的一款最简单方便的简单内存泄漏的工具了,使用之后,会有以下几个问题:

1:LeakCanary的初始化在哪里?

    较早之前使用leankCanary时,在Application中,会有一个初始化的代码,但在后来2.0版本之后,初始化的代码没了,但确不影响我们使用。难道是后续不需要初始化了?
    其实初始化还是有的,只是后续的版本,利用了Android app的启动流程中的机制,将自身的初始化放入的provider中。在app启动时,会优先初始化app中的provider,正是利用这一特性,所以才不用我们写初始化的代码


image.png
2:初始化的provider在哪里?干了什么事情?

     我们可以去github中找一下相关的provider或者在Android Studio引入的leakcanary包中,在相应的mainfest中找一下provider


image.png

从上图已标出初始化的provider为 AppWatchInstaller$MainProcess

注意:在有的文章中,会说初始化的工程是leakcanar-leaksentry下面的LeakSentryInstaller,我也被这些文章弄的差点错了,后来翻看LeakCanary的github各版本修改日志发现,确实以前初始化的provider是LeakSentryInstall,但仅限于在2.0 Alpha各版本中,在2.0 Beta版之后,就统一改了。

image.png
这个大家要注意一些了。
     那究竟初始化都做了些什么。
  fun install(application: Application) {
    checkMainThread()
    if (this::application.isInitialized) {
      return
    }
    SharkLog.logger = DefaultCanaryLog()
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
  }

跟进代码可以发现,这里做了最关键的一步,xxxDestroyWatcher的注册。在xxxDestroyWatcher中,对Activity,Fragment做了相应的保存,并监听它他的生命周期。

3:检查内存泄漏的原理是什么?

     我们在使用WeakReference或PhantomReference时,会发现他们有一个构造方法是这样的:

public class WeakReference<T> extends Reference<T> {
     public WeakReference(T referent) {
        super(referent);
    }
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}

我们发现,第二个构造方法中,需要传入一个ReferenceQueue的构造方法。这个RefereneQueue就是检查内存泄漏用的。当对象被回收之后,就会把对象放到这个ReferenceQueue中来,所以如果Activity在onDestroy后,如果没有被回收,由在队列中就没有该Activity,那么就可以认为存在内存泄漏。

4:LeakCanary是如何检测的?
ActivityDestroyWatcher
private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
          objectWatcher.watch(
              activity, "${activity::class.java.name} received Activity#onDestroy() callback"
          )
        }
      }
    }

从代码中可以看到,当监听到activity的destroy后,就会执行相应的objectWatch.watch来做一些处理,但真正判断内存泄漏的地方却不在这里。
在InternalAppWatcher.install方法中,除了调用xxxDestroyWatch外,还有一段代码onAppWatcherInstalled(application),看代码其实就是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)

    disableDumpHeapInTests()
  }

在这里面初始化了heapDumper,gcTrigger,heapDumpTrigger等对象用于gc和heapDump,同时还实现了OnObjectRetainedListener,并把自己添加到了上面的onObjectRetainedListeners中,以便每个对象moveToRetained的时候,InternalLeakCanary都能获取到onObjectRetained()的回调,回调里就只是回调了heapDumpTrigger.onObjectRetained()方法。看来都是依赖于HeapDumpTrigger这个类。
HeapDumpTrigger的主逻辑在checkRetainedObjects

private fun checkRetainedObjects(reason: String) {
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
      SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
      return
    }

    var retainedReferenceCount = objectWatcher.retainedObjectCount

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

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

    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
      onRetainInstanceListener.onEvent(DebuggerIsAttached)
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_debugger_attached
          )
      )
      scheduleRetainedObjectCheck(
          reason = "debugger is attached",
          rescheduling = true,
          delayMillis = WAIT_FOR_DEBUG_MILLIS
      )
      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(
          reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
          rescheduling = true,
          delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
      )
      return
    }

    SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
    dismissRetainedCountNotification()
    dumpHeap(retainedReferenceCount, retry = true)
  }

那么HeapDumpTrigger主要是下面几个功能:

  • 后台线程轮询当前还存活着的对象
  • 如果存活的对象大于0,那就触发一次GC操作,回收掉没有泄露的对象
  • GC完后,仍然存活着的对象数和预定的对象数相比较,如果多了就调用heapDumper.dumpHeap()方法把对象dump成文件,并交给HeapAnalyzerService去分析
  • 根据存活情况展示通知

相关文章

  • LeakCanary原理浅析

    LeakCanary原理浅析 1.LeakCanary简介 LeakCanary是一个Android和Java的内...

  • LeakCanary 工作原理浅析

    参考链接Java内存问题 及 LeakCanary 原理分析LeakCanary 工作原理浅析 通过 appli...

  • LeakCanary浅析

    一、使用 LeakCanary的使用非常的简单,两行代码搞定。当然也可以主动的添加需要监听的对象;LeakCana...

  • LeakCanary浅析

    说到内存泄漏,就必须提到LeakCanary. 这个利器,很方便的显示出内存泄漏的地方。在用到的过程中好奇怎...

  • LeakCanary浅析

    前提:LeakCanary 版本v2.4; Android 8.0LeakCanary相信很多开发者都用过,也是目...

  • Leakcanary浅析

    本文主要分析内存泄漏的检测原理和如何实现生产环境应用,代码分析基于Leakcanary 1.6版本。 如何检测内存...

  • LeakCanary源码浅析

    LeakCanary是Square公司的找出内存泄露工具 LeakCanary简单介绍 Square 最新发布的一...

  • LeakCanary源码浅析

    在Android开发中最让人们头疼的就是内存泄漏了,今天来介绍一个查看内存是否泄漏的工具LeakCanary,并通...

  • LeakCanary原理浅析

    LeakCanary是Android内存泄漏的框架,作为一个“面试常见问题”,它一定有值得学习的地方,今天我们就讲...

  • LeakCanary 工作原理浅析

    版权声明:本文为 咕咚 原创文章,可以随意转载,但必须在明确位置注明出处。作者博客地址: http://gudon...

网友评论

      本文标题:LeakCanary浅析

      本文链接:https://www.haomeiwen.com/subject/cyaqvktx.html