内存泄漏(Memory Leak)

什么状况下会致使内存泄露(Memory Leak)?

Android 的虚拟机是基于寄存器的Dalvik,它的最大堆大小通常是16M,有的机器为24M。所以咱们所能利用html

的内存空间是有限的。若是咱们的内存占用超过了必定的水平就会出现OutOfMemory 的错误。微信

内存溢出的几点缘由:app


一、资源释放问题
程序代码的问题,长期保持某些资源,如Context、Cursor、IO 流的引用,资源得不到释放形成内存泄露。ide

须要适当的释放资源的状况,这些硬件资源可能包括:视频、音频、相机等。函数

 

二、广播注册后没取消形成的内存泄漏优化

记得在activity销毁的时候必定要取消广播的注册 this


三、static 关键字的使用问题
static 是Java 中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。.net

因此用static 修饰的变量,它的生命周期是很长的,若是用它来引用一些资源耗费过多的实例(Context 的状况最多),线程

这时就要谨慎对待了。视频

public class ClassName {
private static Context mContext;
//省略
}
以上的代码是很危险的,若是将Activity 赋值到mContext 的话。那么即便该Activity 已经onDestroy,

可是因为仍有对象保存它的引用,所以该Activity 依然不会被释放。


咱们举Android 文档中的一个例子。

private static Drawable sBackground;
  @Override
  protected void onCreate(Bundle state) {
  super.onCreate(state);
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);
  setContentView(label);
}
sBackground 是一个静态的变量,可是咱们发现,咱们并无显式的保存Contex 的引用,可是,当Drawable
与View 链接以后,Drawable 就将View 设置为一个回调,因为View 中是包含Context 的引用的,因此,实
际上咱们依然保存了Context 的引用。这个引用链以下:Drawable->TextView->Context
因此,最终该Context 也没有获得释放,发生了内存泄露。
针对static 的解决方案
① 应该尽可能避免static 成员变量引用资源耗费过多的实例,好比Context。
② 此时的Context 尽可能使用ApplicationContext,由于Application 的Context 的生命周期比较长,引用它不会出现内存泄露的问题。
③ 使用WeakReference 代替强引用。好比可使用WeakReference<Context> mContextRef;


四、线程致使内存溢出
线程产生内存泄露的主要缘由在于线程生命周期的不可控。咱们来考虑下面一段代码。

public class MyActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  new MyThread().start();
  }
private class MyThread extends Thread{
  @Override
  public void run() {
  super.run();
  //do somthing
    }
  }
}
这段代码很日常也很简单,是咱们常用的形式。咱们思考一个问题:假设MyThread 的run 函数是一个很费
时的操做,当咱们开启该线程后,将设备的横屏变为了竖屏,通常状况下当屏幕转换时会从新建立Activity,按照我
们的想法,老的Activity 应该会被销毁才对,然而事实上并不是如此。
因为咱们的线程是Activity 的内部类,因此MyThread 中保存了Activity 的一个引用,当MyThread 的run 函
数没有结束时,MyThread 是不会被销毁的,所以它所引用的老的Activity 也不会被销毁,所以就出现了内存泄露的问题。

有些人喜欢用Android 提供的AsyncTask,但事实上AsyncTask 的问题更加严重,Thread 只有在run 函数不结
束时才出现这种内存泄露问题,然而AsyncTask 内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread 对
象的生命周期是不肯定的,是应用程序没法控制的,所以若是AsyncTask 做为Activity 的内部类,就更容易出现内存泄露的问题。
针对这种线程致使的内存泄露问题的解决方案:
①  将线程的内部类,改成静态内部类(由于非静态内部类拥有外部类对象的强引用,而静态类则不拥有)。
在线程内部采用弱引用保存Context 引用。

 

五、Handler内存泄漏
Handler做为内部类存在于Activity中,可是Handler生命周期与Activity生命周期每每并非相同的,好比当Handler对象有Message在排队,则没法释放,进而致使本该释放的Acitivity也没有办法进行回收。 解决办法:

① 在onDestroy里移除msg或callback

② 声明handler为static类,这样内部类就再也不持有外部类的引用了,就不会阻塞Activity的释放

③ 若是内部类实在须要用到外部类的对象,可在其内部声明一个弱引用引用外部类。

 

六、使用application的context来替代activity

这是一个很隐晦的内存泄漏的状况。有一种简单的方法来避免context相关的内存泄漏,最显著地一个是避免context逃出他本身的范围以外,使用Application context,这个context的生存周期和你的应用的生存周期同样长,而不是取决于activity的生存周期。若是你想保持一个长期生存的对象,而且这个对象须要一个context,记得使用application对象。

 

七、集合中对象没清理形成的内存泄漏

咱们一般把一些对象的引用加入到了集合中,当咱们不须要该对象时,并无把它的引用从集合中清理掉,这样这个集合就会愈来愈大。

若是这个集合是static的话,那状况就更严重了。

 

附: 

1. 微信团队原创分享:Android内存泄漏监控和优化技巧总结

2. Android内存泄漏的检测流程、捕捉以及分析

相关文章
相关标签/搜索