LeakCanary原理分析

15 阅读2分钟

LeakCanary_leakcanary使用-CSDN博客

一.LeakCannary原理

1.核心原理就是基于弱引用(WekReference)和引用队列(ReferenceQueue)的联合使用,如果弱应用所引用的对象被垃圾回收器回收,那么虚拟机就会把这个弱引用加入到与之管理的引用队列中.

d36d12c63aa8b6a747a26a5514472bc2.jpg

2.工作流程

ActivityWatcher,FragmentAndViewModelWatcher,ViewModelClearWatcher分别 监听Activity , Fragment,ViewModel的生命周期,当destory或者onCleared的时候,把Activity或者Fragment或者ViewModel 的实例放到WeekReference 中,然后把这个弱引用放到 观察列表中,大概2到3s后,垃圾回收器完成回收后,遍历ReferenceQueue ,如果Activity|Fragment|ViewModel被回收,引用队列就会有值,就会把这个弱引用从观察队列中移除,如果没有值,就说明这个Activity|Fragment|ViewModel发生了内存泄漏,就会把这个弱引用放到保留队列,这样我们直接从保留队列中获取已经泄漏的Activity|Fragment|ViewModel对象列表.

image.png

aef2d17adea3ee49905b042a111fb952.jpg

3. 查看LeakCanary报错日志

image.png

4.代码模拟实现

  • WaterCher对象监控

class Watcher {

    //监控列表
    private val watchMap=mutableMapOf<String, KeyWeakReference<Any?>>()
    //保留列表
    private val retainMap= mutableMapOf<String, KeyWeakReference<Any?>>()
   //当被监控的对象被GC回收后,对应的弱引用会被加入到引用队列
    private val queue = ReferenceQueue<Any?>()

    fun watch(obj:Any?){
        //生成UUID
        var key= UUID.randomUUID().toString()
        //建立弱引用关系,并关联到引用队列
        var reference: KeyWeakReference<Any?> = KeyWeakReference(obj,queue, key )
        //加入到监控列表做登记
        watchMap[key]=reference;
        //过1秒后,去引用队列中查看,如果在引用队列中找到了,说明GC回收成功
        //如果没有,将引用队列加入到保留列表
        val executor = Executors.newSingleThreadExecutor()
        executor.execute {
            Thread.sleep(1000)
            moveToRetainMap(key)
        }
    }

    fun getRetainMap(): MutableMap<String, KeyWeakReference<Any?>>{
       return retainMap;
    }

    private fun Watcher.moveToRetainMap(key: String) {
        var ref: KeyWeakReference<Any>? =null
        do{
            queue.poll()?.also {
                ref= it as KeyWeakReference<Any>?
            }
            //回收成功,没有发生内存泄漏
            ref?.key.let {
                watchMap.remove(it)
                retainMap.remove(it)
            }
            ref=null

        }while (ref!=null)
        //回收不成功,发生了内存泄漏的情况
        watchMap.remove(key)?.also {
            retainMap[key]=it
        }
    }
}
  • KeyWebReference封装弱引用
class KeyWeakReference<T> : WeakReference<T>{
    var key:String
    constructor(referent:T,key: String):super(referent){
        this.key=key;
    }
    constructor(referent:T, q: ReferenceQueue<in T?>, key: String):super(referent,q){
        this.key=key;
    }
    override fun toString(): String {
        return "KeyWeakReference(key=$key)";
    }
}
  • 模拟测试垃圾回收
fun main() {
    var watcher = Watcher();
    var obj: Any? = Object()
    watcher.watch(obj)
    obj = null //弱应用持有的强引用被置为null以后,GC扫描才会回收这个弱应用.
    System.gc()
    Thread.sleep(2000)
    watcher.getRetainMap().forEach { key, reference ->
        {
            println("key:$key,keyWeakReference:$reference ,obj:${reference.get()}")

        }
    }
}