WeakReference和ReferenceQueue
在jdk1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种。
平常加载图片的时候,经常需要把经常显示的图片缓存在内存中来提高加载效率和程序的性能。想象一下如果在jdk1.1时代,我们的Bitmap缓存对象肯定通过强引用来缓存,就无法被GC了,如果缓存一多整个程序就gg了。所以WeakReference就出现了,当一个对象仅仅被WeakReference指向, 而没有任何其他StrongReference指向的时候, 如果GC运行, 那么这个对象就会被回收。所以厂常见的缓存算法LRUCache里面都是通过WeakReference来持有对象的。
我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列了。ReferenceQueue即这样的一个对象,当一个obj被gc掉之后,其相应的包装类,即WeakReference对象会被放入queue中。我们可以从queue中获取到相应的对象信息,同时进行额外的处理。
具体的用法如下:
1 | public static void main(String[] args) { |
LeakCanary
使用
最简单的使用就是在Application的onCreate中一行即搞定
1 |
|
原理
采用无脑跟进法分析!
1 | public static RefWatcher install(Application application) { |
通过AndroidRefWatcherBuilder的buildAndInstall
来创建一个RefWatcher实例
1 | public RefWatcher buildAndInstall() { |
通过ActivityRefWatcher.install
来创建ActivityRefWatcher实例并监听Activity的生命周期
1 | public static void install(Application application, RefWatcher refWatcher) { |
1 | public void watchActivities() { |
通过lifecycleCallbacks来在Activity的生命周期中回调,实际上只在onActivityDestroyed
中做了处理,调用了 ActivityRefWatcher的onActivityDestroyed(activity)方法。
1 | void onActivityDestroyed(Activity activity) { |
跟进
1 | public void watch(Object watchedReference) { |
在watch中把被观察对象watchedReference
封装成带有key值和带引用队列的KeyedWeakReference对象,然后在ensureGoneAsync中的ensureGone进行分析泄露。
泄露分析
1 | Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { |
先来看看removeWeaklyReachableReferences
1 | private void removeWeaklyReachableReferences() { |
KeyedWeakReference是WeakReference子类,这里用到的就是我上面介绍的 通过WeakReference加上ReferenceQueue来实现对已经被gc的对象做额外处理。如果queue.poll()!=null
成立,说明被观察的对象已经被gc掉了,因为只有被gc了相应的包装类WeakReference的实例才会被添加到ReferenceQueue中。
然后通过gone
来判断是否有泄露
1 | private boolean gone(KeyedWeakReference reference) { |
如果有泄露,就手动触发一下gc,再来判断有没有泄露。如果确认泄露了,那么就开始进入内存分析。
内存快照分析
1 | if (!gone(reference)) { |
确定泄露之后就要分析内存快照,根据上面唯一的key来定位,项目内部又使用了另一框架haha来分析。
分析的代码如下:
1 | heapdumpListener.analyze( |
构造器传入的Listener如下
1 | public static RefWatcher install(Application application) { |
所以上面的heapdumpListener就是ServiceHeapDumpListener的实例,看看这类的analyze方法
1 | public void analyze(HeapDump heapDump) { |
实际上就是启动一个IntentService来进行内存分析操作
1 | public final class HeapAnalyzerService extends IntentService { |
跟进
1 | public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) { |
上面的checkForLeak方法就是输入.hprof,输出分析结果,主要有以下几个步骤:
1.把.hprof转为Snapshot,这个Snapshot对象就包含了对象引用的所有路径
2.精简gcroots,把重复的路径删除,重新封装成不重复的路径的容器
3.找出泄漏的对象
4.找出泄漏对象的最短路径