为了使文章尽量通俗易懂。在探究
LeakCanary
之前,有必要补充些
Java
引用的知识。
引用分类
强引用
强引用是使用最普遍的引用。一个对象具有强引用,则在
GC
发生时,该对象将不会回收。当Jvm虚拟机内存空间不足时,虚拟机会抛出OutOfMemoryError
错误,不会回收具有强引用的对象来解决内存不足的问题。
软引用
当一个对象只有软引用,若虚拟机内存空间足够,垃圾回收器就不会回收该对象;
若内存空间不足,下次GC
时这些只有软引用对象将被回收。若垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
在创建软引用实例时,可以传入一个引用队列(ReferenceQueue
)将该软引用与引用队列关联,这样当软引用所引用的对象被垃圾回收器回收前,Jvm虚拟机会把这个软引用加入到与之关联的引用队列中。
弱引用
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在
GC
发生时,若一个对象只有虚引用,不管虚拟机内存空间是否足够,都会回收它的内存。
在创建弱引用实例时,可以传入一个引用队列(ReferenceQueue
)将该软引用与引用队列关联,这样当软引用所引用的对象被垃圾回收器回收前,Jvm虚拟机就会把这个软引用加入到与之关联的引用队列中。
虚引用
虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
在创建虚引用实例时,可以传入一个引用队列(ReferenceQueue
)将该软引用与引用队列关联,这样当软引用所引用的对象被垃圾回收器回收前,Jvm虚拟机就会把这个软引用加入到与之关联的引用队列中。
软引用、弱引用、虚引用的构造方法均可以传入一个ReferenceQueue
与之关联。在引用所指的对象被回收后,引用(reference
)本身将会被加入到ReferenceQueue
之中,此时引用所引用的对象reference.get()
已被回收 (reference
此时不为null
,reference.get()
此时为null)。
所以,在一个非强引用所引用的对象回收时,如果引用reference
没有被加入到被关联的ReferenceQueue
中,则表示还有引用所引用的对象还没有被回收。如果判断一个对象的非强引用本该出现在ReferenceQueue
中,实际上却没有出现,则表示该对象发送内存泄漏。
LeakCanary
理论依据
- 当一个
Activity
的onDestory
方法被执行后,说明该Activity
的生命周期已经走完,在下次GC
发生时,该Activity
对象应将被回收。 - 通过上面对引用的学习,可以考虑在
onDestory
发生时为Activity
创建一个弱引用,并关联一个RefrenceQuence
,当Activity
被正常回收,弱引用应该出现在该RefrenceQuence
中,否则便可以判断该Activity
存在内存泄漏。 - 通过
Application.registerActivityLifecycleCallbacks()
方法可以注册Activity
生命周期的监听,每当一个Activity
调用onDestroy
进行页面销毁时,去获取到这个Activity
的弱引用并关联一个ReferenceQuence
,通过检测ReferenceQuence
中是否存在该弱引用判断这个Activity
对象是否正常回收。 - 当
onDestory
被调用后,初步观察到Activity
未被GC
正常回收时,手动触发一次GC
,由于手动发起GC
请求后并不会立即执行垃圾回收,所以需要在一定时延后再二次确认Activity
是否已经回收,如果再次判断Activity
对象未被回收,则表示Activity
存在内存泄漏。
源码解析
- 在导入依赖后使用如下方法便可以使用
LeakCanary
进行Activity
内存泄漏分析:
//:MyApp.java public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); //判断是否在主进程中 if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } //使用LeakCanary LeakCanary.install(this); } }
LeakCanary 2.0 的初始化放在了自带的ContentProvider中:
ContentProvider的
onCreate
的调用时机介于Application的attachBaseContext
和onCreate
之间,LeakCanary 2.0将LeakCanary的初始化放在了自带的ContentProvider的onCreate
函数中,将multiprocess
设为false
可以保证ContentProvider只初始化一次,LeakCanary也只初始化一次
- 进入
LeakCanary#install(Application application)
//:LeakCanary.java public final class LeakCanary { /** * Creates a {@link RefWatcher} that works out of the box, and starts watching activity * references (on ICS+). */ public static RefWatcher install(Application application) { return refWatcher(application) .listenerServiceClass(DisplayLeakService.class)//内存泄漏后用于显示的线上泄漏信息 .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())//白名单 .buildAndInstall(); } /** Builder to create a customized {@link RefWatcher} with appropriate Android defaults. */ public static AndroidRefWatcherBuilder refWatcher(Context context) { return new AndroidRefWatcherBuilder(context)