常见的Android内存泄漏问题以及解决办法

常见的Android内存泄漏问题以及解决办法

什么是内存泄漏以及其危害

编写代码的时候因为错误或者疏忽,导致一部分内存空间不能被垃圾回收机制回收,造成这部分的内存空间浪费,再也不能被程序使用到,这就叫做内存泄漏。这部分内存并不是消失不见了,而是这部分内存当中存储的数据已经不再使用,但是又占用着位置,导致物理内存空间得不到释放。

内存泄漏会导致程序可使用的内存减少,从而造成OOM(out of memory)等不利因素,造成程序崩溃。

常见内存泄漏以及解决办法

单例模式造成的内存泄漏

一般情况下,单例模式的生命周期是跟应用的生命周期一样长的,所以当单例模式中的对象持有一个需要释放的对象时,这就会导致这个需要释放的对象得不到释放,从而造成内存泄漏。比如如下例子:

class AppFactory{
    private static AppFactory sAppFactory;
    private Context mContext;

    private AppFactory(Context context){
        this.mContext = context;
    }

    public static AppFactory getInstance(Context context){
        if(sAppFactory == null){
            sAppFactory = new AppFactory(context);
        }
        return sAppFactory;
    }
}

当context传入的是一个activity的时候,则会造成内存泄漏,因为当activity关闭的时候,因为单例对象还持有activity的引用,导致这个activity对象不能够被释放回收,从而造成浪费。

解决办法:

1、把传入的activity对象改成applicationContext

2、把上述代码改成如下:

class AppFactory{
    private static AppFactory sAppFactory;
    private Context mContext;

    private AppFactory(Context context){
        this.mContext = context.getApplicationContext();
    }

    public static AppFactory getInstance(Context context){
        if(sAppFactory == null){
            sAppFactory = new AppFactory(context);
        }
        return sAppFactory;
    }
}

不管传入的是什么context,最终都变成了applicationContext,这个生命周期就是应用程序的生命周期,从而避免了内存泄漏。

注: 上面标注了一个“一般情况下”,也就是正常情况下,而非正常情况就是内存不够的时候,系统会把静态对象直接置为null,所以这就不存在持有context的引用了。同时,当要持有持久性数据时,不要用static来保存这个数据。

非静态内部类造成的内存泄漏

很多时候,处于各种原因,我们都会在一个类里面创建另外的类,也就是内部类,不管匿名的还是非匿名的,都会创建一个或者多个。由于内部类会默认持有外部类的一个引用,所以当使用内部类的时候,如果内部类对象没有及时释放外部类的引用,则会造成内存泄漏。

内部类造成的内存泄漏有以下几种情况:

1、非静态内部类的静态对象

代码如下:

class MainActivity extends Activity{

    private static Test sTest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(sTest == null){
            sTest = new Test();
        }
    }

    class Test{
    }

}

这里为了避免重复创建Test对象,所以使用了static关键字。但是因为静态对象的生命周期跟应用程序的生命周期是一样长的,所以当关闭这个activity的时候会造成内存泄漏。

解决办法: 将该内部类拿出来封装成一个单例,如果使用到了Context,则使用applicationContext。

2、Handler造成的内存泄漏
class MainActivity extends Activity{

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

因为Handler是非静态内部类,所以会持有外部类的引用。但是有人会问:这个Handler对象不是静态的,这怎么也会造成内存泄漏呢?Handler里面是有个消息队列的,消息队列里能够存储很多消息,而handler处理消息是要时间的,比如在还有消息需要处理的时候,把activity关闭了,因为Handler还在处理消息,肯定不可能释放掉外部类的引用的,即activity的引用,所以这个就造成了内存泄漏。

解决办法: 首先把handler类定义成静态的,然后显示的持有外部类的引用,但是这个持有不能是强引用,而是使用弱引用,这样当回收时就可以释放外部类的引用了,代码如下:

class MainActivity extends Activity{

    private MyHandler myHandler = new MyHandler(this);

    private static class MyHandler extends Handler {

        private WeakReference<Context> reference;
        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

这样一改之后就可以避免activity的内存泄漏了,但是当还有消息队列里面还有消息或者正在处理最后一个消息时,虽然activity不会内存泄漏了,但是这些剩余的消息或者正在处理的消息会造成一些内存泄漏,所以最好的做法是在onstop方法或者ondestory方法里把这些消息移除掉,代码如下:

class MainActivity extends Activity{

    private MyHandler myHandler = new MyHandler(this);

    private static class MyHandler extends Handler {

        private WeakReference<Activity> reference;
        public MyHandler(Activity activity) {
            reference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Activity activity = reference.get();
            if(activity == null){
                return;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onDestroy() {
       super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
}

使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。

3、activity中线程内部类造成内存泄漏

因为内部类会持有外部类的引用,而线程又是用来处理耗时操作的,所以当线程还在处理耗时操作时就把activity关闭了,这就会造成内存泄漏。

解决办法: 显示的持有外部类的弱引用,这个跟上面的是一样的,只是不需要移除消息队列里的消息。

资源未关闭造成的内存泄漏

对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

Bitmap使用后未释放内存造成的内存泄漏

如果Bitmap造成的内存泄漏,很大的可能会直接导致程序崩溃。所以当一个Bitmap对象不使用时,一定要调用recycle()释放内存。如果Bitmap对象没有释放,那么很容易就会出现OOM,从而导致应用程序崩溃。

集合中的对象未清理掉

我们经常把一些对象的引用加入到集合中,但是当这些对象的引用不用的时候,我们并没有把这些对象清理掉,这样会造成内存泄漏,导致集合的内存越来越大。如果这个集合是static的,那么危害更大。

ListView以及其他使用BaseAdapter的控件造成的内存泄漏

当给ListView设置Adapter的时候会继承BaseAdapter,其中有个方法是public View getView(int position, View convertView, ViewGroup parent),其中第二个参数会被缓存起来,如果在这个方法里面始终创建一个View来返回,那么这个缓存的View就浪费掉了,使用不到,从而造成内存泄漏。而且因为要重新创建一个View,这里会造成性能的消耗,即浪费资源,也浪费时间。

解决办法:判断convertView是否为空,如果为空,则创建这个View,如果不为空,则直接使用convertView,即不会造成内存泄漏,也可以提高性能。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值