LeakCanary的工作原理

LeakCanary是如何做到仅仅使用一句话做到内存泄漏检测的?

在leakcanary的faq.md中有一段解释

How does LeakCanary get installed by only adding a dependency?

On Android, content providers are created after the Application instance is created but before Application.onCreate() is called. The leakcanary-object-watcher-android artifact has a non exported ContentProvider defined in its AndroidManifest.xml file. When that ContentProvider is installed, it adds activity and fragment lifecycle listeners to the application.

翻译过来就是:

在安卓中,ContentProvider被创建的时机处在Application被创建之后,但是在Application.onCreate()方法之前

所以,LeakCanary通过Gradle构建时会合并AndroidManifest.xml的机制,提供了一个ContentProvider在我们的软件里,当它被安装时,它便添加了相关refWatcher.

那么refWatcher是怎么做到的检测内存泄漏呢?

这又扯到我不是很熟悉的领域了

似乎是Java、Kotlin 语言有四种引用类型:强引用, 弱引用,软引用,虚引用,而软引用和弱引用所引用的对象被释放掉之后,就会进入关联的引用队列的尾部。那么我们就能通过这种方式来看这些弱引用的对象是否还存活

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
private fun wefQueueTest() {
    val weakefQueue = ReferenceQueue<Any?>()
    var obj: Any? = Object()
    val weakReference = WeakReference(obj, weakefQueue)
    println("weakReference = $weakReference")

    obj = null // 弱引用的对象释放掉
    Runtime.getRuntime().gc() // 强制垃圾回收
    try {
        Thread.sleep(100)
    } catch (e: Exception) {
        e.printStackTrace()
    }

    var ref: WeakReference<Any?>?
    do {
        ref = weakefQueue.poll() as? WeakReference<Any?>
        val obj1 = ref?.get()
        println("obj1 = $obj1")
        println("$ref in queue")
    } while (ref != null)
}

运行结果

可以看到弱引用对象在引用队列中!

那就可以合理猜测LeakCanary在干什么了,它抢先在Application.onCreate()方法启动之前,然后对每个Activity的onDestoryed()方法注册回调,当一个Activity要被摧毁时,就创建一个对它的弱引用,同时关联一个引用队列。

那么我每间隔几秒就看一下我这个引用队列里有没有东西(如果这个对象成功释放,那么这个弱引用会进入引用队列)

有东西,就说明这个Activity被成功释放,那可以不管它了;没有东西,那问题就严重了,那说么这个Activity 可能存在内存泄漏 那我就要重点关注它了,把它移交到一个新的因素有严查队列中(监管级别更高的队列),每隔一段时间再检查,如果严查队列还没有东西,那我就认为发生了内存泄漏!

原理大致是这么个原理,我源码还没看,等我看完继续更新。。

Common causes for memory leaks

Most memory leaks are caused by bugs related to the lifecycle of objects. Here are a few common Android mistakes:

  • Adding a Fragment instance to the backstack without clearing that Fragment’s view fields in Fragment.onDestroyView() (more details in this StackOverflow answer).
  • Storing an Activity instance as a Context field in an object that survives activity recreation due to configuration changes.
  • Registering a listener, broadcast receiver or RxJava subscription which references an object with lifecycle, and forgetting to unregister when the lifecycle reaches its end.
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计
本站总访问量 本站访客数 人次