Handler的内存泄漏的问题?
所谓的内存泄漏就是这个对象没有任何使用价值了但是由于任然存在引用导致内存被占用了垃圾回收器回收不了。。。。。
至于为什么会出现内存泄漏呢?
这里面的原理是很深很深的,因为new 的handler是一个内部类对象对外部类对象有个隐试强引用。
内部类对外部类有个隐试强引用其实并没有导致内存泄漏,尽管activity的引用被内部类对象持有着,大不了内部类对象先被回收,外部类还是可以被回收的,关键是由于上一篇文章中我特地强调了在Handler内部有个msg.target=this,这个内部类handler被消息队列中的消息所持有,所以只要消息队列里有消息,这个handler就没法被清理,这样就导致外部类的activity即便退出了也没法被gc回收,这样就内存泄漏了。基于我这点说明那么我目前可以从三点来解决这个内存泄漏问题。
引申一下知识点:
Java中的引用有四种,垃圾回收器对垃圾进行回收的时候氛围搜索算法和一个回收算法。
搜素算法使用的是引用计数算法,就是一个对象一个对象遍历,当一个对象没被别人引用的时候计数器会被加1,如果这个引用消失掉,计数器就会减一,当引用计数器为0,ok。垃圾回收期就会将其列为可回收对象,gc运行的时候不定期的将其清理掉,将内存给释放掉。当我们内部类创建的时候对外部类就会有一个隐试的强引用。所谓隐试,就是我们看不见,但是虚拟机设计的就是有个引用
因为是个强引用,所以这个内部类对象永远不能被清理掉,但是当我们退出界面,外部类的MainActivity按理说也应该被清理掉,但是由于存在内部类的引用导致这个activity对象也永远清理不掉,而这个内部类由于MessageQueen里的meg对其有个引用,所以导致handler清理不了,于是activity也就清理不了,所以导致内存泄漏,解决的办法就是把handler设置成静态的,这样静态内部类对外部类就不存在隐试强引用的关系了。
四种引用类型,
1:强引用。当一个对象被强引用引用的时候,GC永远没法将其清理掉,除非oom,虚拟机都挂了,肯定也挂了
2: 软引用,当系统内存不足的时候被GC掉
3:弱引用,当GC发现它的时候就将其干掉。引申一下,假如给这个弱引用加一个queen的话(记得有个重载方法可以加这个参数),被清理以后的引用会被放到这个queen里,然后我们可以通过检测这个queen是否为null观察对象有没有被清理,null说明没被清理,不为null说明被清理了,内存检测公共Leakcanary利用这个原理做的,在多一嘴,内存检测公共还有Mat,DDMS的heap可以检测
4:虚引用,不是一个真正的引用类型,是用来检测内存泄漏的
1:第一种解决方案给handler加静态这样就不存在内部类对外部类隐试强引用问题了
这种提交方式:
2:还有种解决办法就是用软引用。当内存不足的时候还是会清理,就是我们继承handler的类,然后我们创建这个自定义handler对象放进自己new的软引用中,这样内存不足的时候就会把软引用的对象给清理掉
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myhandler2 = new Myhandler2();
softReference = new SoftReference<Handler>(myhandler2);
}
class Myhandler2 extends Handler{
WeakReference<Context> weak;
}
@Override
public void handleMessage(Message msg) {
String s=(String)msg.obj;
Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
super.handleMessage(msg);
}
}
//点击按钮实现的子线程向主线程传消息
public void click(View v){
new Thread(new Runnable() {
@Override
public void run() {
Message msg=new Message();
msg.what=13;
msg.obj="dasdasd";
softReference.get().sendMessage(msg);
}
}).start();
}
3:第三种清理消息队列的消息
就是在activity的destory或者finish的时候调用myhandler2.removeCallbacksAndMessages(null);这样把消息给清空,就不存在meg.target=handler。就业不存在这个MessageQueen的消息持有这个handler的引用了,这样handler就可以被清理了。