介绍
Leakcanary是用于检测内存泄漏的库
在研究源码之前,需要一些jvm的前置知识
JVM如何回收内存
通过可达性算法,将不太容易回收的变量作为gc root,可回收的对象会与其形成一个引用链,如果没有在引用链上,即不可达,也就是没有回收的对象,造成内存泄漏
何为弱引用
弱引用表示对象标记为弱可达,一旦垃圾回收器扫描到,就会被gc,将对象通过WeakReference构造函数传入就可标记为弱可达,注意还有一个参数为ReferenceQueue(以下用RQ代替),是一个引用队列,被回收对象所引用的WeakReference对象会在gc时添加到队列中,可以利用这个特性,在强制gc后,判断如果引用队列中有值,证明发生了gc,没有产生内存泄漏;如果引用队列为空,则证明有内存泄漏产生
(以上其实就是Leakcanary的理论基础)
举个弱引用的例子
static class Num {
int i;
public Num(int i) {
this.i = i;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
Log.d(TAG, "finalize->" + i);
}
}
public static void main(String[] args) {
ReferenceQueue<Object> mRq;
WeakReference<Num> mWeakReference = new WeakReference<>(new Num(126), mRq = new ReferenceQueue<>());
Log.d(TAG, "GC前," + "num=" + mWeakReference.get().i + ",mRq=" + mRq.poll());
Runtime.getRuntime().gc();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "GC后," + "num=" + mWeakReference.get() + ",mRq=" + mRq.poll());
}
输出结果
正式分析
具备了以上前置知识,分析下几个问题
- Leakcanary如果实现检测内存泄漏的
- 如何做初始化工作
如何检测内存泄漏
- 首先会构建一个包含引用对象(比如Activity和Fragemnt或者任何其他对象)和引用队列ReferenceQueue的弱引用对象,并将这个弱引用对象放到一个map中,如果发生了gc,弱引用对象会添加到RQ中
- 此时将map中的值清空,如果仍然存在不为空的值,则就是没有被回收的对象,强制gc
- 如果未回收对象仍然存在,则弹泄漏弹框通知并dump heap文件并通过shark来分析到gc root的最短路径
如何做初始化工作
比较有意思的地方是这个库并没有在项目中需要初始化,其实不然,是通过ContentProvider来进行初始化的,因为ContentProvider的调用时间是介于Application的onCreate和attachBaseContext之间进行的,不过也存在一个问题就是对启动优化不太友好