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就可以被清理了。