性能优化2-常见内存泄漏

首先来确定一下内存泄漏和内存溢出。

  1. 内存泄漏(Memory Leak):进程中某些对面已经没有使用价值,但是他们却还可以直接或间接的被引用到GC Root导致无法回收,当内存泄漏过多时,在加上应用本身使用的内存,长时间就会导致内存溢出OOM,
  2. 内存溢出(OOM ):当应用需要占用的内存资源超过了 Daivik虚拟机所能分配的内存就会内存溢出。

正文开始:

  1. 静态变量导致的:
    例如:在Activity中使用当前的Context创建一个单例。如果 这个Activity被销毁后是要被回收的,但是由于单例持有Activity的引用,导致内存泄漏。
    ps:那我们来思考一种情况,如果这个Activity反复被打开销毁3次或以上,因为每次打开都会生成新的实例,是不是每次打开的实例都不会被回收呢?实际情况是,除了最后一次打开的和第一次打开的,其他的还是会被GC回收。
    正确的使用情况,应该是使用Application的上下文,因为单例的生命周期和Application是一致的。
  2. 非静态内部类( 匿名内部类 )导致的:
    因为非静态内部类(匿名内部类)默认是持有外部类的引用。而静态内部类是不会隐式持有外部类引用。下面来说明一下常用的错误示范:

    public void loadData(){//隐式持有MainActivity实例。MainActivity.this.a
        new Thread(new Runnable() {
            @Override
            public void run() {
            while(true){
                try {
                //int b=a;
                Thread.sleep(1000);
                } catch (InterruptedException e) {
                e.printStackTrace();
                }
            }
            }
        }).start();
        }

    正确写法应该是将loadData()方法改为静态内部类。
    还有一种常用的Handler,错误的示例:

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
             super.handleMessage(msg);
             switch (msg.what){
                 case 0:
                   //加载数据
                   break;
            }
        }
    };

    解决方案:如果handler需要使用MainActivity里的变量,那么就需要写一个类继承Handler,然后通过构造传过来MainActivity的引用,当然,不能使用强引用保存,需要使用软引用保存。有人会说当使用软引用或者弱引用的时候,MainActivity很容易或者可以被GC回收。那么要了解GC回收的机制是什么?当MainActivity不被任何的对象引用的时候才会被回收,如果连MainActivity都被回收了,那他里面的Handler还有什么用?

    private static class MyHandler extends Handler{
    //        private MainActivity mainActivity;//直接持有了一个外部类的强引用,会内存泄露
        private WeakReference<MainActivity> mainActivity;//设置软引用保存,当内存一发生GC的时候就会回收。
    
        public MyHandler(MainActivity mainActivity) {
            this.mainActivity = new WeakReference<MainActivity>(mainActivity);
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity main =  mainActivity.get();
            if(main==null||main.isFinishing()){
                return;
            }
            switch (msg.what){
                case 0:
                    break;
    
            }
        }
    };

3:不需要用的监听未移除会发生内存泄露

tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
            @Override
            public void onWindowFocusChanged(boolean b) {
                //监听view的加载,view加载出来的时候,进行操作。
                //进行一系列操作
                //计算完后,一定要移除这个监听
                tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
            }
        });
  • 这里需要说明的是set监听的时候。监听执行完就会回收对象,而add监听是讲监听放入集合中。

4:资源未关闭引起的内存泄露情况

  • 比如:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute,attr.recycle()回收。listview的adapter没有使用convertView,当不需要使用的时候,要记得及时释放资源。否则就会内存泄露。

5:无限循环动画,这种不常遇到。

  • 没有在onDestroy中停止动画,造成Activity就会变成泄露对象。

简单的介绍了几种常遇到的情况,希望小伙伴们能够避免这些坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值