首先来确定一下内存泄漏和内存溢出。
- 内存泄漏(Memory Leak):进程中某些对面已经没有使用价值,但是他们却还可以直接或间接的被引用到GC Root导致无法回收,当内存泄漏过多时,在加上应用本身使用的内存,长时间就会导致内存溢出OOM,
- 内存溢出(OOM ):当应用需要占用的内存资源超过了 Daivik虚拟机所能分配的内存就会内存溢出。
正文开始:
- 静态变量导致的:
例如:在Activity中使用当前的Context创建一个单例。如果 这个Activity被销毁后是要被回收的,但是由于单例持有Activity的引用,导致内存泄漏。
ps:那我们来思考一种情况,如果这个Activity反复被打开销毁3次或以上,因为每次打开都会生成新的实例,是不是每次打开的实例都不会被回收呢?实际情况是,除了最后一次打开的和第一次打开的,其他的还是会被GC回收。
正确的使用情况,应该是使用Application的上下文,因为单例的生命周期和Application是一致的。 非静态内部类( 匿名内部类 )导致的:
因为非静态内部类(匿名内部类)默认是持有外部类的引用。而静态内部类是不会隐式持有外部类引用。下面来说明一下常用的错误示范:public void loadData(){//隐式持有MainActivity实例。MainActivity.this.a new Thread(new Runnable() { @Override public void run() { while(true){ try { //int b=a; Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); }
正确写法应该是将loadData()方法改为静态内部类。
还有一种常用的Handler,错误的示例:private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 0: //加载数据 break; } } };
解决方案:如果handler需要使用MainActivity里的变量,那么就需要写一个类继承Handler,然后通过构造传过来MainActivity的引用,当然,不能使用强引用保存,需要使用软引用保存。有人会说当使用软引用或者弱引用的时候,MainActivity很容易或者可以被GC回收。那么要了解GC回收的机制是什么?当MainActivity不被任何的对象引用的时候才会被回收,如果连MainActivity都被回收了,那他里面的Handler还有什么用?
private static class MyHandler extends Handler{ // private MainActivity mainActivity;//直接持有了一个外部类的强引用,会内存泄露 private WeakReference<MainActivity> mainActivity;//设置软引用保存,当内存一发生GC的时候就会回收。 public MyHandler(MainActivity mainActivity) { this.mainActivity = new WeakReference<MainActivity>(mainActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity main = mainActivity.get(); if(main==null||main.isFinishing()){ return; } switch (msg.what){ case 0: break; } } };
3:不需要用的监听未移除会发生内存泄露
tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
@Override
public void onWindowFocusChanged(boolean b) {
//监听view的加载,view加载出来的时候,进行操作。
//进行一系列操作
//计算完后,一定要移除这个监听
tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
}
});
- 这里需要说明的是set监听的时候。监听执行完就会回收对象,而add监听是讲监听放入集合中。
4:资源未关闭引起的内存泄露情况
- 比如:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute,attr.recycle()回收。listview的adapter没有使用convertView,当不需要使用的时候,要记得及时释放资源。否则就会内存泄露。
5:无限循环动画,这种不常遇到。
- 没有在onDestroy中停止动画,造成Activity就会变成泄露对象。
简单的介绍了几种常遇到的情况,希望小伙伴们能够避免这些坑。