LeakCanary_leakcanary使用-CSDN博客
一.LeakCannary原理
1.核心原理就是基于弱引用(WekReference)和引用队列(ReferenceQueue)的联合使用,如果弱应用所引用的对象被垃圾回收器回收,那么虚拟机就会把这个弱引用加入到与之管理的引用队列中.
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对象列表.
3. 查看LeakCanary报错日志
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()}")
}
}
}