1.参考文献
http://www.linuxidc.com/Linux/2013-12/94065.htm
2.什么是内存泄露(Memory Leak)
内存泄露是指程序向系统申请内存后,无法释放已申请的内存的内存空间。造成的结果是程序本身无法再访问申请的那块内存,系统也不能将它分配给其他程序。一次内存泄露的危害可以忽略,但多次内存泄露堆积之后,会导致内存使用过高,导致OOM(内存溢出)。
3.Java中的内存回收机制
Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可达状态,则将该对象从内存中回收。所谓的不可达状态是指一个对象不被任何引用所指向。另外如果一组对象中只包含相互的引用,而没有来自它们外部的引用(如有AB两个对象互相持有引用,但没有任何外部对象持有指向A或B的引用),这也属于不可达状态。
4.Android中使用Handler造成内存泄露的原因
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
mImageView.setImageBitmap(bitmap);
}
};
上面是Handler的一个简单使用,当使用内部类来创建Handler的时候,Handler对象会隐式地持有一个外部类对象的引用(否则怎么可能通过Handler操作Activity中的View)。而Handler通常会伴随着一个耗时的后台线程(如从网络加载图片)一起出现,
new Thread(){
@Override
public void run() {
//.....
Message msg = new Message();
mHandler.sendMessage(msg);
}
}.start();
在后台线程执行完毕后,通过消息机制通知Handler,然后Handler把图片更新到界面。
如果用户在网络请求过程中关闭Activity,正常情况下,该Activity不在被使用,就有可能在GC检查时被回收掉但由于这时线程尚未执行完毕,该线程持有Handler的引用,Handler又吃用Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束。
另外如果执行Handler的postDelayed()方法,该方法会将Handler装入到Message中,并把Message加入到MessageQueue中那么在设定delay到达之情,会有一条MessageQueue->Message->Handler->Activity的引用链,也会导致Activity被持有引用而无法回收。
5.使用Handler导致内存泄露的解决方法
1)在关闭Activity的时候停止后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity也就会被回收了。如果Handler是被delay的Message持有了引用,则执行Handler的removeCallbacks()方法,将消息对象从消息队列中移除就可以了。
2)将Handler声明为静态类
静态类不持有外部类的对象,所以Activity可以被随意回收。但是由于Handler不再持有外部类对象的引用,导致无法在Handler中操作Activity中的对象了。所以需要在Handler中增加一个对Activity的弱引用(WeakReferece):
private 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);
}
}
}
6.什么是WeakReference?
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。