一、什么叫内存泄漏、内存溢出?
内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个10M的Bitmap,但系统分配给APP的连续内存不足10M,就会导致内存溢出。
内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致out of memory!
二、LeakCanary使用:
1、增加依赖:
2、LeakCanary核心思想:
1)找出有泄漏嫌疑的对象;
2)确诊 -- 使用可达性分析;
3、LeakCanary启动流程:
1)在LeakCanary源码的mainfest.xml文件里面注册了contentprovide;
2)APP在打包时会合并所有引用的三方库的mainfest.xml文件到APP的mainfest.xml文件;
3)在APP启动时,程序会加载LeakCanary注册的contentprovide,并执行contentprovide的onCreate方法;
4)在contentprovide的onCreate方法里面做初始化工作;
5)通过ActivityLifecycleCallbacks监听activity/Fragment生命周期,自动调用RefWatcher.watch(Object obj)进行内存泄漏检测;
6)如果要检测Object对象,则要手动调用RefWatcher.watch(Object obj)方法;
7)启动一个工作线程用于内存泄漏检测。
各生命周期的自动监听:
Activity的生命周期监听:注册 Application.ActivityLifecycleCallbacks ;
Fragment的生命周期期监听:同样,注册 FragmentManager.FragmentLifecycleCallbacks,但Fragment较为复杂,因为Fragment有三种,即android.app.Fragment、androidx.fragment.app.Fragment、android.support.v4.app.Fragment,因此需要注册各自包下的FragmentManager.FragmentLifecycleCallbacks;
ViewModel的监听:由于ViewModel也是androidx下面的特性,因此其依赖androidx.fragment.app.Fragment的监听;
三、RefWatcher.watch()如何检测是否存在内存泄漏?
1、Java的四种引用
1. 强引用(Strong Reference)
如果一个对象具有强引用,则无论在什么情况下,GC都不会回收被引用的对象。当内存空间不足时,JAVA虚拟机宁可抛出OutOfMemoryError终止应用程序也不会回收具有强引用的对象。
2. 软引用(Soft Reference)
表示一个对象处在有用但非必须的状态。如果一个对象具有软引用,在内存空间充足时,GC就不会回收该对象;当内存空间不足时,GC会回收该对象的内存(回收发生在OutOfMemoryError之前)。
3. 弱引用(Weak Reference)
用来描述非必须的对象。它类似软引用,但是强度比软引用更弱一些:弱引用具有更短的生命.GC在扫描的过程中,一旦发现只具有被弱引用关联的对象,都会回收掉被弱引用关联的对象。换言之,无论当前内存是否紧缺,GC都将回收被弱引用关联的对象。
4. 虚引用(Phantom Reference)
虚引等同于没有引用,这意味着在任何时候都可能被GC回收,设置虚引用的目的是为了被虚引用关联的对象在被垃圾回收器回收时,能够收到一个系统通知。
2、ReferenceQueue
ReferenceQueue引用队列,存放引用的队列,保存Reference对象。其作用在于Reference对象所引用的对象被GC回收时,该Reference对象将会被加入引用队列中(ReferenceQueue)的队列末尾。
3、GCRoots与可达性分析
通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所有的引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
4、RefWatcher.watch检测内存泄漏的逻辑
1.创建WeakReference对象(弱引用容器)引用object对象(被监控对象);
2.当触发GC回收操作时,如果object对象被回收,则会将WeakReference对象移到ReferenceQueue队列末尾;也就是说,当触发GC回收操作时,判断WeakReference对象是否移到ReferenceQueue队列,没有,则表示object对象没有被回收,object对象可能存在泄漏。
四、总结
1)LeakCanary是通过在Application的registerActivityLifecycleCallbacks方法实现对Activity销毁监听的,该方法主要用来统一管理所有activity的生命周期。所有Activity在销毁时在其OnDestory方法中都会回调ActivityLifecycleCallbacks#onActivityDestroyed方法,而LeakCanary要做的就是在该方法中调用RefWatcher#watch方法实现对activity进行内存泄漏监控。
2)LeakCanary利用了Java的引用与垃圾回收机制,即WeakReference和ReferenceQueue,通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果能够被回收,则说明引用可达,垃圾回收器就会将该WeakReference引用存放到ReferenceQueue中。假如我们要监视某个activity对象,LeakCanary就会去ReferenceQueue找这个对象的引用,如果找到了,说明该对象是引用可达的,能被GC回收,如果没有找到,说明该对象有可能发生了内存泄漏。
3)LeakCanary会将Java堆转储到一个.hprof文件中,再使用自己实现的hprof解析器分析.hprof文件并定位堆转储中“滞留”的对象,并对每个"滞留"的对象找出 GC roots 的最短强引用路径,并确定是否是泄露,如果泄漏,建立导致泄露的引用链。最后,再将分析完毕的结果以通知的形式展现出来。