内存泄露是指当一个对象不再使用的时候,本该被回收,而被其他对象所持有导致该对象无法被GC回收,这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。
内存泄漏(Memory Leak)
进程中某些对象已经没有使用的价值了,但是他们却还可以直接或间接地被引用到GC Root导致无法回收。当内存泄漏过多的时候,再加上应用本身占用的内存,最终可能就会导致内存溢出OOM。
内存溢出(OOM)
当应用的heap资源超过了Dalvik虚拟机分配的内存就会内存溢出
应用卡顿
应用异常(oom)
错误写法:
Single single=Single.getInstance(this);
当调用getInstance时,如果传入的context是Activity的context,只有这个单例没有被释放,那么这个activity就会一直持有这个引用,直到进程退出才会释放。
public class Single {
private static Single instance;
private Context context;
private Single(Context context) {
this.context = context;
}
public static Single getInstance(Context context) {
if (instance == null) {
synchronized (Single.class) {
if (instance == null)
instance = new Single(context);
}
}
return instance;
}
}
改进后写法:
Single single=Single.getInstance(getApplicationContext());
尽量使用Application的Context就不要使用Activity的Content,Application的生命周期伴随着整个进程的周期。
错误写法:
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
改进后写法:
在Activity销毁时及时关闭或者注销。
错误写法:
private static NoStaticMethod method;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (method!=null)
method=new NoStaticMethod();
}
class NoStaticMethod{
}
改进后写法:
将非静态内部类修改为静态内部类。(静态内部类不会隐式持有外部类)
错误写法:
mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
改进后写法:
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息。
public static class MyHandler extends Handler {
private WeakReference weakReference;
public MyHandler(MainActivity activity) {
weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = weakReference.get();
if (activity != null) {
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
错误写法:
异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。
new Thread(new Runnable() {
@Override
public void run() {
while (true){
Log.d(TAG, "--------------");
}
}
});
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
return null;
}
}.execute();
改进后写法:
使用 静态内部类,避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源。
private static class MyThread extends Thread {
private WeakReference mWeak;
public MyThread(MainActivity activity) {
mWeak = new WeakReference(activity);
}
@Override
public void run() {
super.run();
while (true) {
if (mWeak == null) return;
if (mWeak.get() != null) {
Log.d(TAG, "--------------");
}
}
}
}
1.我们故意写一个内存泄露的代码,即:
Single.getInstance(getApplicationContext());
运行程序并退出(finish)
我们可以看出是Sinle类出现了问题,原因我们也知道就是因为单例的问题,现在我们改一下代码:
Single.getInstance(getApplicationContext());
再看:
果然没有问题了
以上这些只是一些常见的造成内存泄露的原因,在实践的路上我们还任重而道远,我们可以通过AndroidStudio自带的工具来检查是否存在内存泄露,也可以通过添加第三方库来检查 。