“内存泄漏”就是一个对象已经不须要再使用了,可是由于其它的对象持有该对象的引用,致使它的内存不能被回收。“内存泄漏”的慢慢积累,最终会致使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);
总结