Android性能优化--内存泄露

概述:

内存泄露是指当一个对象不再使用的时候,本该被回收,而被其他对象所持有导致该对象无法被GC回收,这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。

内存泄漏与内存溢出的区别

内存泄漏(Memory Leak)
进程中某些对象已经没有使用的价值了,但是他们却还可以直接或间接地被引用到GC Root导致无法回收。当内存泄漏过多的时候,再加上应用本身占用的内存,最终可能就会导致内存溢出OOM。

内存溢出(OOM)
当应用的heap资源超过了Dalvik虚拟机分配的内存就会内存溢出

内存泄漏带来的影响

应用卡顿

应用异常(oom)

开发过程中经常遇到的:

1.单例造成的内存泄露

错误写法:
Single single=Single.getInstance(this);
当调用getInstance时,如果传入的context是Activity的context,只有这个单例没有被释放,那么这个activity就会一直持有这个引用,直到进程退出才会释放。

public class Single {

    private static Single instance;

    private Context context;

    private Single(Context context) {
        this.context = context;
    }

    public static Single getInstance(Context context) {
        if (instance == null) {
            synchronized (Single.class) {
                if (instance == null)
                    instance = new Single(context);
            }
        }
        return instance;
    }
}

改进后写法:
Single single=Single.getInstance(getApplicationContext());
尽量使用Application的Context就不要使用Activity的Content,Application的生命周期伴随着整个进程的周期。

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

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

3.非静态内部类创建静态实例造成的内存泄漏

错误写法:

 private static NoStaticMethod method;

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

    class NoStaticMethod{

    }

改进后写法:
将非静态内部类修改为静态内部类。(静态内部类不会隐式持有外部类)

4.Handelr造成的内存泄露

错误写法:
mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

 private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

改进后写法:
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息。

 public static class MyHandler extends Handler {
        private WeakReference<MainActivity> weakReference;

        public MyHandler(MainActivity activity) {
            weakReference = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity activity = weakReference.get();
            if (activity != null) {

            }
        }
    }
 @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
    }
5.线程造成的内存泄漏

错误写法:
异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。

 new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    Log.d(TAG, "--------------");
                }
            }
        });

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

            @Override
            protected Void doInBackground(Void... params) {
                return null;
            }
        }.execute();

改进后写法:
使用 静态内部类,避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源。


    private static class MyThread extends Thread {
        private WeakReference<MainActivity> mWeak;

        public MyThread(MainActivity activity) {
            mWeak = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void run() {
            super.run();
            while (true) {
                if (mWeak == null) return;
                if (mWeak.get() != null) {
                    Log.d(TAG, "--------------");
                }

            }
        }
    }
6.注册了系统的服务,但onDestory未注销

AS查看内存泄露

1.我们故意写一个内存泄露的代码,即:
Single.getInstance(getApplicationContext());

运行程序并退出(finish)

这里写图片描述

这里写图片描述

进行分析
这里写图片描述

这里写图片描述
我们可以看出是Sinle类出现了问题,原因我们也知道就是因为单例的问题,现在我们改一下代码:
Single.getInstance(getApplicationContext());
再看:
这里写图片描述

果然没有问题了

小结

以上这些只是一些常见的造成内存泄露的原因,在实践的路上我们还任重而道远,我们可以通过AndroidStudio自带的工具来检查是否存在内存泄露,也可以通过添加第三方库来检查 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值