Leakedcanary原理

Posted by pxfile Blog on June 15, 2018

面试题-LeakedCanary原理

如果检测到有内存泄漏,手机桌面会多出一个图标,点进去查看可以看见泄漏信息。

图-1 内存泄漏示意图

检测原理

监听

  1. ActivityRefWatcher中,注册Activity生命周期监听接口,当Activity onDestroy()被调用时,将当前Activity加入内存泄漏监听队列;

在Android中,当一个Activity走完onDestroy生命周期后,说明该页面已经被销毁了,应该被系统GC回收。通过Application.registerActivityLifecycleCallbacks()方法注册Activity生命周期的监听,每当一个Activity页面销毁时候,获取到这个Activity去检测这个Activity是否真的被系统GC。

检测

  1. RefWatcher中,watch方法将对象用WeakReference引起来,监听对象是否内存泄漏

这里的原理是:WeakReferenceReferenceQueue<Object>配合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列

检测方法就很简单了,主动GC,触发WeakReference被GC,同时检测GC前后,ReferenceQueue是否包含被监听对象;如果不包含,则说明该对象没有被GC,一定存在到GC Roots的强引用链,也就是发生了泄漏。

当获取了待分析的对象后,需要确定这个对象是否产生了内存泄漏。

通过WeakReference + ReferenceQueue来判断对象是否被系统GC回收,WeakReference 创建时,可以传入一个 ReferenceQueue 对象。当被 WeakReference 引用的对象的生命周期结束,一旦被 GC 检查到,GC 将会把该对象添加到 ReferenceQueue 中,待ReferenceQueue处理。当 GC 过后对象一直不被加入 ReferenceQueue,它可能存在内存泄漏。

当我们初步确定待分析对象未被GC回收时候,手动触发GC,二次确认。

源码分析

检测过程主要分为两个部分

  • 监听Activity销毁,并判断是否存在内存泄漏
  • 找到内存泄漏的对象的引用路径

判断是否存在内存泄漏

从上面Activity生命周期监听回调可以看见,每次当Activity执行完onDestroy生命周期,会调用RefWatcher去分析是否存在内存泄漏。

如果被系统回收了,直接就返回DONE;如果没有被系统回收,可能存在内存泄漏,为了保证结果的准确性,调用gcTrigger.runGc();,手动触发系统GC,然后再尝试移除待分析对象,如果还存在,说明存在内存泄漏。

确定有内存泄漏后,是发送了一个Intent,启动了IntentService服务,启动Service后,会在onHandleIntent分析,找到内存泄漏对象的引用关系。

找到引用关系

启动分析泄漏的服务后,分析最终得到结果这里用到了Square的另一个库haha,哈哈哈哈哈,名字真的就是叫这个,开源地址:https://github.com/square/haha

首先获取到内存中的heap堆快照,然后对快照做了去重处理,去除一些重复的强引用关系。拿到待分析的类,去快照里面找引用关系,并将结果返回。

服务拿到分析结果后,启动了另一个IntentService服务,服务启动后,判断是否需要将内存泄漏信息存到本地,如果需要就存到本地,然后设置消息通知的基本信息,通过LeakCanaryInternals.showNotification方法调用系统自身的通知栏通知,告诉用户应用有内存泄漏。

总结

LeakCanary对于内存泄漏的检测非常有效,但也并不是所有的内存泄漏都能检测出来。

  • 无法检测出Service中的内存泄漏问题
  • 如果最底层的MainActivity一直未走onDestroy生命周期(它在Activity栈的最底层),无法检测出它的调用栈的内存泄漏。