美文网首页
内存泄漏

内存泄漏

作者: 小虫虫奇遇记 | 来源:发表于2020-08-10 10:22 被阅读0次

    什么是内存泄漏?

    对象在其生命周期之外因为被引用导致无法被回收,就会造成内存泄漏。
    即某个对象已经不需要再用但又不能被回收,一直在内存中占用着空间,而导致它无法被回收的原因大多是由于它被一个生命周期更长的对象所引用。

    引用:栈
    对象:堆
    堆中的对象在生命周期结束后仍被引用导致无法回收,无法释放内存空间。

    常见的内存泄漏case:

    非静态内部类持有外部类的引用
    单例/静态对象的生命周期和App的生命周期一致,也容易造成泄漏
    匿名内部类,都会持有 外部类MainActivity 的引用

    When the activity is finished, the delayed message will continue to live in the main thread’s message queue for 10 minutes before it is processed. The message holds a reference to the activity’s Handler, and the Handler holds an implicit reference to its outer class (the SampleActivity, in this case). This reference will persist until the message is processed, thus preventing the activity context from being garbage collected and leaking all of the application’s resources.

    Handler 和 Runnable 作为匿名内部类,都会持有 MainActivity 的引用,而它们内部有一个 10 秒钟的定时器,如果在打开 MainActivity 的 10 秒内关闭了 MainActivity,那么由于 Looper的生命周期和App一样长,looper又持有未处理的message,导致Handler 和 Runnable 的生命周期比 MainActivity 长,会导致 MainActivity 无法被回收,从而造成内存泄漏。
    引用关系:Looper--->未处理的消息Message-->Handler-->Activity
    Looper的生命周期和App一样长,looper又持有未处理的message,在Handler消息队列 还有未处理的消息 / 正在处理消息时,消息队列中的Message持有Handler实例的引用(因为要调用 handler回调处理消息)
    由于Handler = 非静态内部类 / 匿名内部类(2种使用方式),故又默认持有外部类的引用(即MainActivity实例)
    那么应该如何避免内存泄漏呢?
    两种方案:a.静态内部类+弱引用: Handler 和 Runnable 定义为静态内部类,这样它们就不再持有 MainActivity 的引用了,从而避免了内存泄漏;
    b: 再在 onDestory 时调用 handler 的 removeCallbacks 方法来移除 Message,这样不但能避免内存泄漏,而且在退出 Activity 时取消了定时器,保证 10 秒以后也不会执行 run 方法.
    静态内部类不依赖任何外部类实例,也不是一开始初始化外部类时就创建的,实例化静态内部类对象时才能调用。
    如果 Handler 或者 Runnable 中持有 静态Context 对象成员,那么即使使用静态内部类,还是会发生内存泄漏. 避免:弱引用;

        private static class TestHandler extends Handler { 
            private Context context; 
            private TestHandler(WeakReference<Context> weakContext) { 
                context = weakContext.get(); 
            } 
        } 
    
    public class Simple {
        Object object;
        public void method1(){
            object = new Object();
        //...其他代码
        }
    }
    

    这里的object实例,其实我们期望它只作用于method1()方法中,且其他地方不会再用到它,但是,当method1()方法执行完成后,object对象所分配的内存不会马上被认为是可以被释放的对象,只有在Simple类创建的对象被释放后才会被释放,严格的说,这就是一种内存泄露。解决方法就是将object作为method1()方法中的局部变量。

    弱引用作用:尽快回收内存,避免内存泄漏。

    LeakCanary 原理,为什么检测内存泄漏需要两次?
    答:为需要监测的对象建立弱引用。ReferenceQueue弱引用队列,每次WeakReference所指向的对象被GC后,这个弱引用都会被放入这个与之相关联的ReferenceQueue队列中。如果在预期的时间内依然没被回收,就认为发生了内存泄漏。
    为了避免因为gc不及时带来的误判,leakcanay会进行二次确认进行保证。

     //如果内存依旧没被释放,则再给一次gc的机会
        gcTrigger.runGc();
        //再次移除
        removeWeaklyReachableReferences();
        if (!gone(reference)) {
         //走到这里,认为内存确实泄露了...
        // dump文件进行分析  LeakCanary在dump出hprof文件后,会启动一个IntentService进行分析:HeapAnalyzerService在分析出结果之后会启动DisplayLeakService用来发起Notification 以及将结果记录下来写在文件里面。
    }
    

    参考:https://www.jianshu.com/p/5ee6b471970e

    相关文章

      网友评论

          本文标题:内存泄漏

          本文链接:https://www.haomeiwen.com/subject/kneldktx.html