LeakCanary是Square公司推出个一个内存泄露检测工具,
地址(https://square.github.io/leakcanary)
使用
简单使用,
在项目Model的build.gradle文件中dependencies{}中加入
debugImplementation 'com.squareup.leakcanary:leakcanary-android:version'
然后在你的App中的onCreate()方法中添加
LeakCanary.install(this);
这样LeakCanary就可以监控系统的Activity的内存泄露了,默认使用只能检测到Activity的内存泄露。
如果你想检测其他对象的内存泄露,可以自定义监控对象。
refWatcher = LeakCanary.install(this);
LeakCanary.install(this)
方法返回一个RefWatcher
对象,调用该队的的watch()方法就行了。
该方法的签名如下
//传入你想检测的对象
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
原理
LeakCanary 中对内存泄漏检测的核心原理就是基于 WeakReference 和 ReferenceQueue 实现的。
1. 当一个 Activity 需要被回收时,就将其包装到一个 WeakReference 中,并且在 WeakReference 的构造器中传入自定义的 ReferenceQueue。
2. 然后给包装后的 WeakReference 做一个标记 Key,并且在一个强引用 Set 中添加相应的 Key 记录
3. 最后主动触发 GC,遍历自定义 ReferenceQueue 中所有的记录,并根据获取的 Reference 对象将 Set 中的记录也删除
经过上面 3 步之后,还保留在 Set 中的就是:应当被 GC 回收,但是实际还保留在内存中的对象,也就是发生泄漏了的对象
public static RefWatcher install(Application application) {
return install(application, DisplayLeakService.class);
}
public static RefWatcher install(Application application,
Class extends AbstractAnalysisResultService> listenerServiceClass) {
if (isInAnalyzerProcess(application)) {
return RefWatcher.DISABLED;
}
enableDisplayLeakActivity(application);
HeapDump.Listener heapDumpListener =
new ServiceHeapDumpListener(application, listenerServiceClass);
RefWatcher refWatcher = androidWatcher(application, heapDumpListener);
// LeakCanary 中监听 Activity 生命周期是由 ActivityRefWatch 来负责的,主要是通过注册 Android 系统提供的 ActivityLifecycleCallbacks,来监听 Activity 的生命周期方法的调用,
ActivityRefWatcher.installOnIcsPlus(application, refWatcher);//默认只检测Activity的内存泄露
return refWatcher;//返回refWatcher对象,
}
RefWatcher对象的构造方法如下。
private final Executor watchExecutor; //线程池
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;//GC触发器
private final HeapDumper heapDumper;
private final Set retainedKeys; //检测对象的Key集合
private final ReferenceQueue
最后调用refWatcher对象的watch方法。最后回调它的重载方法。
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
public void watch(Object watchedReference, String referenceName) {
//对检测对象和引用名称判空
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
if (debuggerControl.isDebuggerAttached()) {
return;
}
//检测开始时间
final long watchStartNanoTime = System.nanoTime();
//随机生成Key
String key = UUID.randomUUID().toString();
//将Key放入retainedKeys集合中,retainedKeys是一个CopyOnWriteArraySet对象。
retainedKeys.add(key);
//将检测对象,key,referenceName,queue 构建一个KeyedWeakReference对象
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
//向线程池watchExecutor提交一个任务。在该任务里启动检测。
watchExecutor.execute(new Runnable() {
@Override public void run() {
ensureGone(reference, watchStartNanoTime);
}
});
}
void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//移出引用队列里的对象
removeWeaklyReachableReferences();
if (gone(reference) || debuggerControl.isDebuggerAttached()) {
return;
}
//启动垃圾回收
gcTrigger.runGc();
//移出引用队列里的对象
removeWeaklyReachableReferences();
//如果retainedKeys里边依然包含改若引用对象的key.说明该弱对象引用的对象泄露了。
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
//生成Dump文件
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == null) {
// Could not dump the heap, abort.
return;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,
heapDumpDurationMs));
}
}
//移出如引用对象
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
//垃圾回收启动之前,一旦弱引用对象引用的对象变得弱引用时,弱引用对象就会加入到它内部的队列里。
KeyedWeakReference ref;
//遍历队列,如果队列里的元素等于当前的对象,说明当前对象被回收了,就从retainedKeys里移出弱引用对象对一个的key.
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
//判断当前的reference对象对象的Key是否包含在retainedKeys,如果在就说明reference没有被垃圾回收
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}