浅谈 Android 内存溢出与内存泄漏
概念
内存溢出(Out of memory):系统会给每个APP分配内存,默认16M内存,每个手机厂商的默认值不一样,当APP所需要的内存大于了系统分配的内存,就会造成内存溢出;内存溢出就是分配的内存被用光了,不够用了。
内存泄漏(Memory leak):当一个对象不再使用了,本应该被垃圾回收器(GC)回收,但是这个对象由于被其他正在使用的对象所持有,造成无法被回收,导致一部分内存一直被占着。
内存泄漏和内存溢出的关系:内存泄露过多会导致内存溢出。内存泄露导致一部分内存没有被回收,一直被占着,可利用内存变少了,当泄露过多 时,可利用的内存越来越少,就会引起内存溢出了。
原因及优化
内存溢出
-
Bitmap使用不当
1、使用Bitmap对象要用recycle释放
2、使用软引用、弱引用
3、使用图片缓存技术(例如:LruCache)
内存泄漏
在内存比较低的系统上对大量的图片、音频、视频处理
优化: 建议使用第三方,或者JNI来进行处理Handler使用不当
例如:
在日常开发中我们通常都是这样定义Handler对象:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
dosomething();
}
};
但是这样也存在着一个隐藏的问题:在Activity中使用Handler创建匿名内部类会隐式的持有外部Activity对象的引用,当子线程使用Handler暂时无法完成异步任务时,handler对象无法销毁,同时由于隐式的持有activity对象的引用,造成activity对象以及相关的组件与资源文件同样无法销毁,造成内存泄露。我们普通的Handler,平时使用时也是可以不用考虑这些的,但是当你在子线程中有耗时操作通知UI时,要考虑到这一点,以免activity结束时子线程持有Handler对象,导致Activity无法被释放。
优化解决方法:
1)使用静态变量定义Handler
static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
dosomething();
}
};
这样的话,handler对象由于是静态类型无法持有外部activity的引用,但是这样Handler不再持有外部类的引用,导致程序不允许在Handler中操作activity中的对象了,这时候我们需要在Handler对象中增加一个队activity的弱引用;
2)Handler使用弱引用
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
最后在ondestory方法中将后面的消息移除
在activity执行onDestory时,判断是否有handler已经执行完成,否则做相关逻辑操作;
mhanHandler.removeCallbacksAndMessages(null);
-
Context对象使用不当
例如自定义单例对象
public class Single {
private static Single instance;
private Context context;
private Object obj = new Object();
private Single(Context context) {
this.context = context;
}
public static Single getInstance(Context context) {
if (instance == null) {
synchronized(obj) {
if (instance == null) {
instance = new Single(context);
}
}
}
return instance;
}
}
优化解决方法:使用全局的的Context或者context.getApplication();
单列模式应该尽量少持有生命周期不同的外部对象,一旦持有该对象的时候,必须在该对象的生命周期结束前制null
-
非静态内部类,静态实例化
static关键字修饰的变量由于生命周期过长,容易造成内存泄漏
例如:
public class MyActivity extends AppCompatActivity {
/** 静态成员变量*/
public static InnerClass innerClass = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
innerClass = new InnerClass();
}
class InnerClass {
public void test() {
//TODO ...
}
}
}
这里内部类InnerClass隐式的持有外部类MyActivity的引用,而在MyActivity的onCreate方法中调用了 innerClass = new InnerClass();
这样innerClass就会在MyActivity创建的时候是有了他的引用,而innerClass是静态类型的不会被垃圾回收,MyActivity在执行onDestory方法的时候由于被innerClass持有了引用而无法被回收,所以这样MyActivity就总是被innerClass持有而无法回收造成内存泄露。
优化解决方法:尽量少使用静态变量,一定要使用要及时进行制null处理
属性动画或循环动画使用不当
优化解决方法:在Activity中使用了属性循环动画,在onDestroy()方法中未正确停止动画BraodcastReceiver、File、Cursor等资源的使用未及时关闭
优化解决方法:在销毁activity时,应该及时销毁或者回收框架使用了注册方法而未反注册
例如:
使用的事件总线框架-EventBus,当我们需要注册某个Activity时需要在onCreate中:
EventBus.getDefault().register(this);
然后这样之后就没有其他操作的话就会出现内存泄露的情况,因为EventBus对象会是有该Activity的引用,即使执行了改Activity的onDestory方法,由于被EventBus隐式的持有了该对象的引用,造成其无法被回收。
优化解决方法:需要在onDestory方法中执行反注册:
EventBus.getDefault().unregister(this);
参考文章
https://blog.csdn.net/qq_28607155/article/details/76148989
https://blog.csdn.net/u014674558/article/details/62234008
https://blog.csdn.net/guolin_blog/article/details/9316683