1. 程式人生 > >關於匿名內部類,非靜態內部類會造成記憶體洩露的隱患。

關於匿名內部類,非靜態內部類會造成記憶體洩露的隱患。

注意是記憶體洩露,不是記憶體溢位。啊吐舌頭

首先先看一下下面這樣一段程式碼

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
@Override
public void run() {
while (true) {
                SystemClock.sleep(1000);
}
        }
    }).start()

;
}

Thread 是一個匿名內部類,,當我們finish的時候,該activity例項不會真正銷燬,GC機制也不會進行該例項的垃圾回收,因為匿名內部類和非靜態內部類持有外部類的強引用,也就是說Thread持有外部activity的強引用,而thread內部while(true)是死迴圈,執行緒不會停止,對外部activity的強引用也不會消失。這樣就造成了 memory leak,記憶體溢位。通常情況下我們可以設定一個flag,在activity,的生命週期ondestroy中改變flag的狀態,但是!!!主執行緒和子執行緒的執行時有執行緒排程的,也就是說會造成競爭的現象,兩個執行緒會爭奪cup時間片的執行權,額。。越挖越深,這又會造成我們雖然改變的flag的狀態,但是,子執行緒中的flag並不是馬上就能改變值,因為jvm memory model,原型和執行原理,所有的執行緒都是在自己的工作記憶體中工作的,對值得操作是先從主記憶體讀取到工作執行緒的工作記憶體中,然後cpu對工作記憶體的值進行修改,最後再寫回主記憶體,所以主執行緒對flag的操作可能恰好的發生在子執行緒已經讀完主記憶體的值到工作記憶體,但是還沒有執行的這段時間,所以,我們應該讓flag的狀態改變能夠讓子執行緒馬上可見,應該在宣告flag的時候加上

volatile關鍵字,然該關鍵字不能保證操作的原子性,但是能夠保證變數flag的可見性。當然,還有,我們可以宣告一個靜態類。但是!!!慎用靜態類與靜態變數,比如當我們旋轉螢幕,可能會造成activity的銷燬與重建(雖然國內很多應用不允許旋轉螢幕),不希望重新載入圖片,所以,有些人會這樣寫private static Drawable background;然後,這樣:ImageView imageView = new ImageView(MainActivity.this);

imageView.setBackground(background);很聰明,這樣無論旋轉多少次螢幕,bitmap都不會重新載入了,省去了大量的時間,但是!!!

imageView 引用了MainActivity的context。imageView.setBackground(background);這句原始碼中(父類View中)background.setCallback(this);background又引用了imageView的回撥強引用,這樣旋轉螢幕,就會造成之前的MainActivity不能被銷燬和回收,他的生命甚至和static一樣長,所以,記憶體洩露了。記得Android官方博有個經典的例子和這個很像。當然,還有很多情況會造成記憶體洩露,譬如,我們經常這樣宣告一個匿名內部handler:private Handler handler newHandler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};和匿名內部執行緒thread一樣,會造成內部洩露的隱患。