内存泄漏问题老生常谈,很常见也很难根治,今天我在这里总结一下内存泄漏的原因和解决方法:
所谓内存泄漏,就是本该被回收的对象,由于某些原因不能被回收,继续占用堆内存的这种状态,导致的结果也是显而易见的,会占用我们本可以使用的内存空间,当超出允许的内存时会引起OOM崩溃。
导致内存泄漏的原因大致分为:
- 集合类
- static修饰的成员变量
- 资源对象使用后未被关闭
- 非静态内部类/匿名类
1.集合类引起的内存泄漏:
当一个对象添加至集合中的时候,该对象会被集合引用到,当想要释放的时候不能释放,只有在集合被销毁的时候该对象才可以被释放
1 // 通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List 2 List<Object> objectList = new ArrayList<>(); 3 for (int i = 0; i < 10; i++) { 4 Object o = new Object(); 5 objectList.add(o); 6 o = null; 7 } 8 // 虽释放了集合元素引用的本身:o=null) 9 // 但集合List 仍然引用该对象,故垃圾回收器GC 依然不可回收该对象
解决方法:
因为是集合把对象引用了,那只需要吧集合释放掉集合对元素的引用也会被释放
//释放集合
objectList .clear(); objectList = null;
2.static修饰的成员变量:
因为被static修饰的成员变量生命周期 = 应用程序的生命周期,会在类被创建的时候加载到内存
static静态成员变量的生命周期>该类的生命周期导致该类没法释放
public class SecondActivity extends Activity{ private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); SecondActivity.this.finish(); this.removeMessages(0); } }; private static Haha haha; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); haha = new Haha(); mHandler.sendEmptyMessageDelayed(0,2000); } class Haha{ } }
像上面这种情况SecondActivity是无法被释放掉的,只有把该静态成员变量置为null,这时就不会导致内存泄漏
protected void onDestroy() { super.onDestroy(); if(haha!=null){ haha = null; } }
更常见的问题是在我们使用单例模式的时候:
public class LeakSingleInstance { private static LeakSingleInstance mInstance; private Context mContext; private LeakSingleInstance(Context mContext){ this.mContext = mContext; } public static LeakSingleInstance getInstance(Context mContext) { if(mInstance == null) { mInstance = new LeakSingleInstance(mContext); } return mInstance; } }
这个在创建的时候AndroidStudio就提示有内存泄漏了,原因显而易见的,我们的单例模式的类是长期存在的,生命周期 = 应用生命周期,我们如果传入了上下文会导致该上下文长期被持有,不被释放,导致内存泄漏
解决方法可以使用:
mContext.getApplicationContext()
直接获取ApplicationContext的上下文使用,但是ApplicationContext使用是有局限性的,使用的时候要注意。
3.资源对象使用后未被关闭
对于资源的使用(如 广播BraodcastReceiver
、文件流File
、数据库游标Cursor
、图片资源Bitmap
等),若在Activity
销毁时无及时关闭 / 注销这些资源,则这些资源将不会被回收,从而造成内存泄漏
// 对于 广播BraodcastReceiver:注销注册 unregisterReceiver() // 对于 文件流File:关闭流 InputStream / OutputStream.close() // 对于 数据库游标cursor:使用后关闭游标 cursor.close() // 对于 图片资源Bitmap:Android分配给图片的内存只有8M,若1个Bitmap对象占内存较多,当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存;最后再赋为null Bitmap.recycle(); Bitmap = null; // 对于动画(属性动画) // 将动画设置成无限循环播放repeatCount = “infinite”后 // 在Activity退出时记得停止动画
还有非静态内部类和匿名类引起的泄漏,留在下篇再分析
by:Jungle张轶