内存泄漏解决方案

“内存泄漏”就是一个对象已经不须要再使用了,可是由于其它的对象持有该对象的引用,致使它的内存不能被回收。“内存泄漏”的慢慢积累,最终会致使OOM “内存溢出”的发生,千里之堤,毁于蚁穴。因此在写代码的过程当中,应该要注意规避会致使“内存泄漏”的代码写法,提升软件的健壮性。java

1、常见致使“内存泄漏”的代码写法及解决方案android

1.静态变量引发的内存泄漏数组

在java中静态变量的生命周期是在类加载时开始,类卸载时结束。换句话说,在android中其生命周期是在进程启动时开始,进程死亡时结束。因此在程序的运行期间,若是进程没有被杀死,静态变量就会一直存在,不会被回收掉。若是静态变量强引用了某个Activity中变量,那么这个Activity就一样也不会被释放,即使是该Activity执行了onDestroy(不要将执行onDestroy和被回收划等号)。缓存

这类问题的解决方案为:1.寻找与该静态变量生命周期差很少的替代对象。2.若找不到,将强引用方式改为弱引用。ide

比较典型的例子以下:
单例引发的Context内存泄漏布局

public class IMManager {
  private Context context;
  private static IMManager mInstance;

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

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

}

当调用getInstance时,若是传入的context是Activity的context。只要这个单例没有被释放,这个Activity也不会被释放。fetch

解决方案this

传入Application的context,由于Application的context的生命周期比Activity长,能够理解为Application的context与单例的生命周期同样长,传入它是最合适的。spa

public class IMManager {
  private Context context;
  private static IMManager mInstance;

  public static IMManager getInstance(Context context) {
    if (mInstance == null) {
      synchronized (IMManager.class) {
        if (mInstance == null)
          //将传入的context转换成Application的context
          mInstance = new IMManager(context.getApplicationContext());
      }
    }
    return mInstance;
  }

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

}

二、非静态内部类引发的内存泄漏线程

在java中,建立一个非静态的内部类实例,就会引用它的外围实例。若是这个非静态内部类实例作了一些耗时的操做,就会形成外围对象不会被回收,从而致使内存泄漏。

这类问题的解决方案为:1.将内部类变成静态内部类 2.若是有强引用Activity中的属性,则将该属性的引用方式改成弱引用。3.在业务容许的状况下,当Activity执行onDestory时,结束这些耗时任务。

内部线程形成的内存泄漏

public class LeakAty extends Activity {

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

  public void test() {
    //匿名内部类会引用其外围实例LeakAty.this,因此会致使内存泄漏
    new Thread(new Runnable() {

      @Override
      public void run() {
        while (true) {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }).start();
  }
  }

解决方案

将非静态匿名内部类修改成静态匿名内部类

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.aty_leak);
    test();
  }
  //加上static,变成静态匿名内部类
  public static void test() {
    new Thread(new Runnable() {

      @Override
      public void run() {
        while (true) {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }).start();
  }
}

Handler引发的内存泄漏

public class LeakAty extends Activity {

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

  }

  private Handler mHandler = new Handler() {
    public void handleMessage(android.os.Message msg) {
      switch (msg.what) {
      case 0:
        // 刷新数据
        break;
      default:
        break;
      }

    };
  };

  private void fetchData() {
    //获取数据
    mHandler.sendEmptyMessage(0);
  }
}

mHandler 为匿名内部类实例,会引用外围对象LeakAty.this,若是该Handler在Activity退出时依然还有消息须要处理,那么这个Activity就不会被回收。

解决方案

public class LeakAty extends Activity {
  private TextView tvResult;
  private MyHandler handler;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.aty_leak);
    tvResult = (TextView) findViewById(R.id.tvResult);
    handler = new MyHandler(this);
    fetchData();

  }
  //第一步,将Handler改为静态内部类。
  private static class MyHandler extends Handler {
    //第二步,将须要引用Activity的地方,改为弱引用。
    private WeakReference<LeakAty> atyInstance;
    public MyHandler(LeakAty aty) {
      this.atyInstance = new WeakReference<LeakAty>(aty);
    }

    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      LeakAty aty = atyInstance == null ? null : atyInstance.get();
      //若是Activity被释放回收了,则不处理这些消息
      if (aty == null||aty.isFinishing()) {
        return;
      }
      aty.tvResult.setText("fetch data success");
    }
  }

  private void fetchData() {
    // 获取数据
    handler.sendEmptyMessage(0);
  }

  @Override
  protected void onDestroy() {
    //第三步,在Activity退出的时候移除回调
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
  }
}

三、资源未关闭引发的内存泄漏
当使用了BraodcastReceiver、File、Cursor、Bitmap等资源时,当不须要使用时,须要及时释放掉,若没有释放,则会引发内存泄漏。

 

四、构造Adapter时,没有使用缓存的 convertView

以构造ListView的BaseAdapter为例,在BaseAdapter中提共了方法:

public View getView(intposition, View convertView, ViewGroup parent)

来向ListView提供每个item所须要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化必定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,而后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。

由此能够看出,若是咱们不去使用convertView,而是每次都在getView()中从新实例化一个View对象的话,即浪费时间,也形成内存垃圾,给垃圾回收增长压力,若是垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,形成没必要要的内存开支。

 

五、一些不良代码成内存压力

 

有些代码并不形成内存泄露,可是它们或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配形成很大影响的,容易迫使虚拟机不得不给该应用进程分配更多的内存,增长vm的负担,形成没必要要的内存开支。

如Bitmap使用不当

第1、及时的销毁。

虽然,系统可以确认Bitmap分配的内存最终会被销毁,可是因为它占用的内存过多,因此极可能会超过Java堆的限制。所以,在用完Bitmap时,要及时的recycle掉。recycle并不能肯定当即就会将Bitmap释放掉,可是会给虚拟机一个暗示:“该图片能够释放了”。

第2、设置必定的采样率。

有时候,咱们要显示的区域很小,没有必要将整个图片都加载出来,而只须要记载一个缩小过的图片,这时候能够设置必定的采样率,那么就能够大大减少占用的内存。以下面的代码:

private ImageView preview;
BitmapFactory.Options options = newBitmapFactory.Options();
options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
Bitmap bitmap =BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);
preview.setImageBitmap(bitmap);   

总结

  • 对 Activity 等组件的引用应该控制在 Activity 的生命周期以内; 若是不能就考虑使用 getApplicationContext 或者 getApplication,以免 Activity 被外部长生命周期的对象引用而泄露。
  • 尽可能不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即便要使用,也要考虑适时把外部成员变量置空;也能够在内部类中使用弱引用来引用外部类的变量。
  • 对于生命周期比Activity长的内部类对象,而且内部类中使用了外部类的成员变量,能够这样作避免内存泄漏:
    • 将内部类改成静态内部类
    • 静态内部类中使用弱引用来引用外部类的成员变量
  • Handler 的持有的引用对象最好使用弱引用,资源释放时也能够清空 Handler 里面的消息。好比在 Activity onStop 或者 onDestroy 的时候,取消掉该 Handler 对象的 Message和 Runnable.
  • 在 Java 的实现过程当中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为 null,好比使用完Bitmap 后先调用 recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用 array.clear() ; array = null)等,最好遵循谁建立谁释放的原则。
  • 正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
  • 保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
相关文章
相关标签/搜索