1. 常见内存泄漏
造成内存泄漏的根本原因
生命周期较短的某个对象被生命周期更长的对象所持有,导致该对象不能及时释放。
1.1 静态变量导致的内存泄漏
因为静态变量生命周期等于应用程序的生命周期,所以静态变量引用的变量不会被回收掉。这里涉及到 GC Roots 的概念。
下面是两种明显的内存泄漏:
======================================================================
private static Context sContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_coupon);
sContext = this;
}
}
// or
public class MyCouponActivity extends BaseActivity {
private static View sView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_coupon);
sView = new View(this);
}
}
======================================================================
1.2 非静态内部类(匿名类)内存泄露
注意一下静态匿名内部类和非静态匿名内部类的区别
非静态匿名内部类会持有外部class的强引用。
Handler 需要使用 static 修饰,且持有 Activity 时需要持有 WeakReference的缘故
1.2.1 HANDLER 内存泄漏
使用非静态内部类来实现Handler,lint就会给出警告。这涉及到Handler的原理。
如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏。
首先,非静态的Handler类会默认持有外部类的引用,包含Activity等
然后,还未处理完的消息(Message)中会持有Handler的引用
还未处理完的消息会处于消息队列中,即消息队列MessageQueue会持有Message的引用
消息队列MessageQueue位于Looper中,Looper的生命周期跟应用一致
因此,此时的引用关系链是Looper -> MessageQueue -> Message -> Handler -> Activity。所以,这时退出Activity的话,由于存在上述的引用关系,垃圾回收器将无法回收Activity,从而造成内存泄漏。
1.2.2 多线程引起的内存泄露
我们一般使用匿名类等来启动一个线程,如下:
======================================================================
@Override
public void run() {
}
}).start();
======================================================================
同样,匿名 Thread 类里持有了外部类的引用。当 Activity 退出时,Thread有可能还在后台执行,这时就会发生了内存泄露。
解决方案和上面 Handler 类似:要不就是变成
静态内部类,引用外面资源时使用WeakReference
要不就是在Activity退出时,结束线程。
1.3 其他情况造成的内存泄漏
集合类内存泄露
集合类添加元素后,将会持有元素对象的引用,导致该元素对象不能被垃圾回收,从而发生内存泄漏
属性动画导致的内存泄漏
属性动画中有一类无限循环的动画,如果Activity中播放此类动画且没有在onDestory方法中去停止动画,那么动画会一直播放下去。我们需要在Activity#onDestory中调用animator.cancel()方法来停止动画。
网络、文件等流忘记关闭
手动注册广播时,退出时忘记unregisterReceiver()
Service执行完后忘记stopSelf()
EventBus等观察者模式的框架忘记手动解除注册
2 内存管理
No comments:
Post a Comment