最近在项目中遇到了内存泄漏的问题,发现LeakCanary报出的引用链看不懂,没办法看不懂报出的错误就先研究一下源代码吧。通过查看LeakCanary的源代码发现这个东西的原理和弱引用有关系,之前也一直没搞懂软引用、弱引用和虚引用到底有什么卵用,今天算是真正看到了他们的用处了。
引用的类型
强引用:这个不用说了,一般我们都是使用这种引用的,只要一个对象有强引用那么jvm一定不会回收这个对象,如果内存不够用,jvm宁愿报OutOfMemoryError也不会回收对象的。
软引用 SoftReference:这种引用一般也不会回收。如果内存不够用,那么就会回收掉。
弱引用 WeakReference:这种引用只要发生了GC就会被回收,LeakCanary其实就是基于这个类实现的。
虚引用 PhantomReference:这个引用和WeakReference差不多,只是这个类的get方法总是返回null,而不像上面其他的引用get方法返回的是内部保存的对象。
上面三种对象在被垃圾收集器回收时都会把他们加入到一个队列中去,前提是初始化的时候传入一个ReferenceQueue
:
//创建queue
ReferenceQueue queue = new ReferenceQueue();
//创建弱引用,并且传入queue,此时queue为空
WeakReference reference = new WeakReference(new Object(), queue);
System.out.println(reference.get()); //返回对象
System.gc(); //GC
Reference reference1 = null;
//GC之后,弱引用被回收,reference 被添加进queue,这是remove就可以获得reference1 对象
try {
reference1 = queue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reference1);
通过上面就可以知道某个对象是否被回收了,如果被回收,那么remove返回不为null,反之则为null。
LeakCanary就是利用了上面虚引用特点,首先LeakCanary在application中注册一个ActivityLifecycleCallbacks
,这个回调中都是和Activity的生命周期相关的,如果application中的Activity生命周期发生变化,就会对应的回调这里面的方法,LeakCanary只是监视Activity的onDestroy,当Activity被destroy掉之后,LeakCanary就会开始监视这个Activity了,具体的核心方法:
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//首先清空reference队列
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//查看reference是否在队列中
if (gone(reference)) {
return DONE;
}
//如果还在的话,那么先触发一次GC,因为当前Activity是被LeakCanary用弱引用引用的,如果GC发生之后,弱引用被加入了队列,说明Activity被销毁
gcTrigger.runGc();
//再此查询队列
removeWeaklyReachableReferences();
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
注释中就是LeakCanary大致原理了
另外,LeakCanary只是注册了Activity生命周期的相关回调哦,所以只能检测到Activity、Fragment的泄露,至于Service、BroadCast或者其他的就无能为力了