LeakCanary原理分析

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


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

aef2d17adea3ee49905b042a111fb952.jpg
  • 查看LeakCanary报错日志


    image.png

代码实现

  • 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()}")

        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容