Android性能优化篇之内存优化--内存泄漏

介绍

本文主要介绍内存泄漏的产生原因分析,常见的导致内存泄漏的示例,以及内存泄漏优化的方法1

什么是内存泄露

当一个对象已经不需要在使用了,本应该被回收,而另一个正在使用的对象持有它的引用,导致对象不能被回收。因为不能被及时回收的本该被回收的内存,就产生了内存泄漏。如果内存泄漏太多会导致程序没有办法申请内存,最后出现内存溢出的错误。

android中导致内存泄漏的主要几种情况

  • 使用单例模式
  • 使用匿名内部类
  • 使用异步事件处理机制Handler
  • 使用静态变量
  • 资源未关闭
  • 设置监听
  • 使用AsyncTask
  • 使用Bitmap

1.单例模式

单例模式是我们在开发过程中经常使用的一种设计模式,但是使用的不当也会造成内存泄露的产生,下面举个例子

private static ComonUtil mInstance = null;
private Context mContext = null;
public ComonUtil(Context context) {
    mContext = context;
}

public static ComonUtil getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new ComonUtil(context);
    }
    return mInstance;
}

我们看到上面的代码就是我们平时使用的单例模式,当然这里没有考虑线程安全,请忽略。当我们传递进来的是Context,那么当前对象就会持有第一次实例化的Context,如果Context是Activity对象,那么就会产生内存泄漏。因为当前对象ComonUtil是静态的,生命周期和应用是一样的,只有应用退出才会释放,导致Activity不能及时释放,带来内存泄漏。

怎么解决呢?
常见的有两种方式,第一就是传入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。

public ComonUtil(Context context) {
    mContext = context.getApplicationContext();
}

2.使用非静态内部类

/**
 * 非静态内部类
 */
public void createNonStaticInnerClass(){
    CustomThread mCustomThread = new CustomThread();
    mCustomThread.start();
}

public class CustomThread extends Thread{
    @Override
    public void run() {
        super.run();
        while (true){
            try {
                Thread.sleep(5000);
                Log.i(TAG,"CustomThread ------- 打印");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我们就以线程为例,当Activity调用了createNonStaticInnerClass方法,然后退出当前Activity时,因为线程还在后台执行且当前线程持有Activity引用,只有等到线程执行完毕,Activitiy才能得到释放,导致内存泄漏。
常用的解决方法有很多,第一把线程类声明为静态的类,如果要用到Activity对象,那么就作为参数传入且为WeakReference,第二在Activity的onDestroy时,停止线程的执行。

public static class CustomThread extends Thread{
    private WeakReference<MainActivity> mActivity;
    public CustomThread(MainActivity activity){
        mActivity = new WeakReference<MainActivity>(activity)
    }
}

3.使用异步事件处理机制Handler

/**
 * 异步消息处理机制  -- handler机制
 */
public void createHandler(){
    mHandler.sendEmptyMessage(0);
}
public Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        //处理耗时操作   
        return false;
    }
});

这个应该是我们平时使用最多的一种方式,如果当handler中处理的是耗时操作,或者当前消息队列中消息很多时,那当Activity退出时,当前message中持有handler的引用,handler又持有Activity的引用,导致Activity不能及时的释放,引起内存泄漏的问题。
解决handler引起的内存泄漏问题常用的两种方式:
1.和上面解决Thread的方式一样,
2.在onDestroy中调用mHandler.removeCallbacksAndMessages(null)

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

4.使用静态变量

同单例引起的内存泄漏。

5.资源未关闭

常见的就是数据库游标没有关闭,对象文件流没有关闭,主要记得关闭就OK了。

扫描二维码关注公众号,回复: 4620990 查看本文章

6.设置监听

常见的是在观察者模式中出现,我们在退出Acviity时没有取消监听,导致被观察者还持有当前Activity的引用,从而引起内存泄漏。
常见的解决方法就是在onPause中注消监听

7.使用AsyncTask

public AsyncTask<Object, Object, Object> mTask = new AsyncTask<Object, Object, Object>() {

    @Override
    protected Object doInBackground(Object... params) {
        //耗时操作
        return null;
    }

    @Override
    protected void onPostExecute(Object o) {
    
    }   
};

和上面同样的道理,匿名内部类持有外部类的引用,AsyncTask耗时操作导致Activity不能及时释放,引起内存泄漏。
解决方法同上:
1.声明为静态类,
2.在onPause中取消任务

8.使用Bitmap

我们知道当bitmap对象没有被使用(引用),gc会回收bitmap的占用内存,当时这边的内存指的是java层的,那么本地内存的释放呢?我们可以通过调用bitmap.recycle()来释放C层上的内存,防止本地内存泄漏


  1. 转载说明:
    链接:https://www.jianshu.com/p/797395731747 ↩︎

猜你喜欢

转载自blog.csdn.net/wangchao8110/article/details/85207819