android内存泄露深入研究

首先抄上百科

隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

Android应用层开发绝大部分能产生的内存泄露都是隐式内存泄露,因为应用结束后内存都能释放的。

我们这里以比较主流的LeakCanary检测android内存泄露的方法来分析它对内存泄露的判定方法。

LeakCanary认为对象的生命周期结束时还没释放对象就认为是内存泄露,而activity的生命周期结束就是调用onDestroy的时候了。但LeakCanary会给你gc的时间,这个时间默认是5S。也就是说,当activity说自己的生命周期已经到头了,我LeakCanary再给你5S时间处理后事,如果没处理完,我就认为你是内存泄露了。


 

private void test() {

    new AsyncTask<Void, Void, Void>() {

      …………………………………..
    }

}

看上面代码Android Studio中报的警告

This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask) less... (Ctrl+F1)

A static field will leak contexts.  Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected.  Similarly, direct field references to activities and fragments from these longer running instances can cause leaks.  ViewModel classes should never point to Views or non-application Contexts.

其实就是内部类会持有外部类的引用,当Activity要结束时,AsyncTask 正在执行(没有cancel,或doInBackground正在执行),此时就认为有可能泄露了。而LeakCanary判定doInBackground的执行时间超过5s就认为属于内存泄露。

 

那么我们用LeakCanary再来验证下以下用法是否会造成内存泄漏

public void test() {

    new Thread() {

        @Override

        public void run() {

            super.run();

            try {

                sleep(10000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }.start();

}

调用这个方法后按返回结束界面,LeakCanary果然直接报内存泄漏。

好吧,这就尴尬了啊。线程里面本来就是用来处理耗时任务,现在你告诉我这样都算内存泄露,那能怎么办呢?只有用static处理么,static常驻内存就不说了,静态类不能访问外部类的非静态成员,我一个个变量的传过去还不是得增加引用,这些引用还不是被占着。好吧,这里又提供了一个弱引用,弱引用需要在判断对象没被回收的时候才继续进行操作。好吧,终于有了个终极解决方案。

问题解决了,现在我们分析下解决这个问题付出的代价。

  1. 额外的常驻内存的对象。
  2. 额外的临时内存对象的创建以及弱引用的get()非空判断。

 

 

结论:事实上Google原生应用中有很多Handler,线程,或异步的使用中都没有进行这种隐式内存泄漏的规避。因为实际上执行里面包含的代码片段的最多执行时间也就那么几秒或几百毫秒,绝大部分情况下即便出现了此类内存泄露的场景,在极短的时间内也能回收这些内存。对用户的使用并不会造成影响。而且你能确定当用户关闭当前界面时,用户就不希望他之前进行的耗时事件仍然继续完成么。其实很多时候程序的健壮性与极致的流畅性是站在对立面,此时又该如何取舍?就像javaC++谁更好这个问题一样。归根结底只有实用的,能让用户获得最好体验的才是正道。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值