1、举例
假设我们去某小区,那么门卫对我们的盘问就类似于LeakCanary
1、进门登记身份证。那么你则进入了保安的观察列表
2、如果我们约定一次只能停留1个小时,那么我1小时后再看你是否登记出来了,如果还是没有,则把你加入怀疑列表
3、如果怀疑列表中的人大于5了,用大喇叭喊到你要出来了,然后保安就通知警察,看是不是坏人。
4、警察确认是坏人,则抓人。
2、实际
身份证:被观察对象的key(uuid)
保安:RefWatcher
观察列表:watchedReferences
怀疑列表:retainReferences
大喇叭:GC
警察:HAHA库
1、以activity为例,Activity调用onDestory后,UUID生成key。被KeyWeakRreference包装,和referenceQueue关联。那key和KeyWeakRreference。存到观察列表watchedReferences。(如果被引用的对象activity被回收则引用队列referenceQueue会收到该对象的弱应用KeyWeakRreference,以此判断对象是否被回收)
2、过了5秒后,调用moveToRetained方法,先判断是否释放了该activtiy,因为被释放了,则观察列表和怀疑列表都会清除它,如果过了这么些时间还是再观察列表里,如果没有释放,则从“观察列表”watchedReferences上升到“怀疑列表”retainReferences
3、如果retainReferences超过5,先调用GC,再请HAHA库去分析dump之后的heap内存
4、HAHA确认内存泄漏对象
3、图解
4、代码
Watcher.java
package com.example.lib.leak;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.BiConsumer;
public class Watcher {
//观察列表
private HashMap<String, KeyWeakReference> watchedReferences = new HashMap<>();
//怀疑列表
private HashMap<String, KeyWeakReference> retainedReferences = new HashMap<>();
//引用队列,相当于一个监视器设备,所有需要监视的对象,盛放监视对象的容器 都与之关联
//当被监视的对象被gc回收后,对应的容器就会被加入到queue
private ReferenceQueue queue = new ReferenceQueue();
public Watcher() {
}
/**
* 清理观察列表和怀疑列表的引用容器
*/
private void removeWeaklyReachableReferences() {
System.out.println("清理列表...");
KeyWeakReference findRef = null;
do {
findRef = (KeyWeakReference) queue.poll();
//不为空说明对应的对象被gc回收了,那么可以把对应的容器从观察列表,怀疑列表移除
System.out.println("findRef = " + findRef);
if (findRef != null) {
System.out.println("打印对应的对象的key: " + findRef.getKey());
//根据key把观察列表中对应的容器移除
Reference removedRef = watchedReferences.remove(findRef.getKey());
//如果removedRef为空,那么有可能被放入到怀疑列表了
//TODO: 思考什么情况下会出现这么现象?
//那么尝试从怀疑列表中移除
if (removedRef == null) {
retainedReferences.remove(findRef.getKey());
}
}
} while (findRef != null);//把所有放到referenceQueue的引用容器找出来
}
/**
* 根据key把对应的容器加入到怀疑列表
*
* @param key
*/
private synchronized void moveToRetained(String key) {
System.out.println("加入到怀疑列表...");
//在加入怀疑列表之前,做一次清理工作
removeWeaklyReachableReferences();
//根据key从观察列表中去找盛放对象的容器,如果被找到,说明到目前为止key对应的对象还没被是否
KeyWeakReference retainedRef = watchedReferences.remove(key);
if (retainedRef != null) {
//把从观察列表中移除出来的对象加入到怀疑列表
retainedReferences.put(key, retainedRef);
}
}
public void watch(Object watchedReference, String referenceName) {
System.out.println("开始watch对象...");
//1. 在没有被监视之前,先清理下观察列表和怀疑列表
removeWeaklyReachableReferences();
//2. 为要监视的对象生成一个唯一的uuid
//相当于把要监视的对象 和容器 与 引用队列建立联系
final String key = UUID.randomUUID().toString();
System.out.println("待监视对象的key: " + key);
//3. 让watchedReference 与一个KeyWeakReference建立一对一映射关系,并与引用队列queue关联
KeyWeakReference reference = new KeyWeakReference(watchedReference, queue, key, "");
//4. 加入到观察列表
watchedReferences.put(key, reference);
//5.过5秒后去看是否还在观察列表,如果还在,则加入到怀疑列表
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
@Override
public void run() {
Utils.sleep(5000);
Watcher.this.moveToRetained(key);
}
});
}
public HashMap<String, KeyWeakReference> getRetainedReferences() {
retainedReferences.forEach(new BiConsumer<String, KeyWeakReference>() {
@Override
public void accept(String key, KeyWeakReference keyWeakReference) {
System.out.println("key: " + key + " , obj: " + keyWeakReference.get() + " , keyWeakReference: " + keyWeakReference);
}
}
);
return retainedReferences;
}
}
、、、、、、、、、、、、、、、、、、、、、、、
Utils
package com.example.lib.leak;
public class Utils {
public static void sleep(long millis){
System.out.println("sleep: " + millis);
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void gc(){
System.out.println("执行gc...");
//主要这里不是使用System.gc,因为它仅仅是通知系统在合适的时间进行一次垃圾回收操作
//实际上并不保证一定执行
Runtime.getRuntime().gc();
sleep(100);
System.runFinalization();
}
}
KeyWeakReference
package com.example.lib.leak;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* 继承自WeakReference,并且加入一个key,用来通过可以key可以查找到对应的KeyWeakReference
* @param <T>
*/
public class KeyWeakReference<T> extends WeakReference<T> {
private String key;
private String name;
public KeyWeakReference(T referent, String key, String name) {
super(referent);
this.key = key;
this.name = name;
}
public KeyWeakReference(T referent, ReferenceQueue<? super T> q, String key, String name) {
super(referent, q);
this.key = key;
this.name = name;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("KeyWeakReference{");
sb.append("key='").append(key).append(''');
sb.append(", name='").append(name).append(''');
sb.append('}');
return sb.toString();
}
}
leakcanaryTest
package com.example.lib.leak;
public class leakcanaryTest {
public static void main(String[] args) {
Watcher watcher = new Watcher();
Object obj = new Object();
System.out.println("obj: " + obj);
watcher.watch(obj,"");
Utils.sleep(500);
//释放对象
obj = null;
Utils.gc();
//TODO: 思考如何判断被观察的对象可能存在泄漏嫌疑
Utils.sleep(5000);
System.out.println("查看是否在怀疑列表:" + watcher.getRetainedReferences().size());
}
}