引用類型賦值為null與加速垃圾回收

作者: 陸敏技  來源: 博客園  發布時間: 2011-04-19 10:36  閱讀: 2387 次  推薦: 3   原文鏈接   [收藏]  
摘要:有一些人認為等于null可以幫助垃圾回收機制早點發現并標識對象是垃圾。其他人則認為這沒有任何幫助。是否賦值為null的問題首先在方法的內部被人提起。現在,為了更好的闡述提出的問題,我們來撰寫一個Winform窗體應用程序

  在標準的Dispose模式中,提到了需要及時釋放資源,卻并沒有進一步細說讓引用等于null是否有必要。

  有一些人認為等于null可以幫助垃圾回收機制早點發現并標識對象是垃圾。其他人則認為這沒有任何幫助。是否賦值為null的問題首先在方法的內部被人提起。現在,為了更好的闡述提出的問題,我們來撰寫一個Winform窗體應用程序。如下:

 
private void button1_Click(object sender, EventArgs e)
{
Method1();
Method2();
}


private void button2_Click(object sender, EventArgs e)
{
GC.Collect();
}


private void Method1()
{
SimpleClass s
= new SimpleClass("method1");
s
= null;
//其它無關工作代碼(這條注釋源于回應回復的朋友的質疑)
}

private void Method2()
{
SimpleClass s
= new SimpleClass("method2");
}
}


class SimpleClass
{

string m_text;

public SimpleClass(string text)
{
m_text
= text;
}


~SimpleClass()
{
MessageBox.Show(
string.Format("SimpleClass Disposed, tag:{0}", m_text));
}
}

  先點擊按鈕1,再點擊按鈕2釋放,我們會發現:

  q 方法Method2中的對象先被釋放,雖然它在Method1之后被調用;

  q 方法Method2中的對象先被釋放,雖然它不像Method1那樣為對象引用賦值為null;

  在CLR托管應用程序中,存在一個根的概念,類型的靜態字段、方法參數以及局部變量都可以作為根存在(值類型不能作為根,只有引用類型的指針才能作為根)。

  上面的兩個方法中各自的局部變量,在代碼運行過程中會在內存中各自創建一個根.在一次垃圾回收中,垃圾回收器會沿著線程棧上行檢查根。檢查到方法內的根時,如果發現沒有任何一個地方引用了局部變量,則不管是否為變量賦值為null,都意味著該根已經被停止掉。然后垃圾回收器發現該根的引用為空,同時標記該根可被釋放,這也表示著Simple類型對象所占用的內存空間可被釋放。所以,在上面的這個例子中,為s指定為null絲毫沒有意義(方法的參數變量也是這種情況)。

  更進一步的事實是,JIT編譯器是一個經過優化的編譯器,無論我們是否在方法內部為局部變量賦值為null,該語句都會被忽略掉

s = null;

  在我們將項目設置為Release模式下,上面的這行代碼將根本不會被編譯進運行時內。

  正式由于上面這樣的分析,很多人認為為對象賦值為null完全沒有必要。但是,在另外一種情況下,卻要注意及時為變量賦值為null。那就是類型的靜態字段。為類型對象賦值為null,并不意味著同時為類型的靜態字段賦值為null:

 
private void button1_Click(object sender, EventArgs e)
{
SimpleClass s
= new SimpleClass("test");
}


private void button2_Click(object sender, EventArgs e)
{
GC.Collect();
}
}


class SimpleClass
{

static AnotherSimpleClass asc = new AnotherSimpleClass();
string m_text;

public SimpleClass(string text)
{
m_text
= text;
}


~SimpleClass()
{

//asc = null;
MessageBox.Show(string.Format("SimpleClass Disposed, tag:{0}", m_text));
}
}


class AnotherSimpleClass
{

~AnotherSimpleClass()
{
MessageBox.Show(
"AnotherSimpleClass Disposed");
}
}

  以上代碼運行的結果使我們發現,當執行垃圾回收,當類型SampleClass對象被回收的時候,類型的靜態字段asc并沒有被回收。

  必須要將SimpleClass的終結器中注釋的那條代碼啟用。

  字段asc才能被正確釋放(注意,要點擊兩次釋放按鈕。這是因為一次垃圾回收會僅僅首先執行終結器)。之所以靜態字段不被釋放(同時賦值為null語句也不會像局部變量那樣被運行時編譯器優化掉),是因為類型的靜態字段一旦被創建,該根就一直存在。所以垃圾回收器始終不會認為它是一個垃圾。非靜態字段不存在這個問題。將asc改為非靜態,再次運行上面的代碼,會發現asc隨著類型的釋放而被釋放。

  上文代碼的例子中,讓asc=null是在終結器中完成的,實際工作中,一旦我們感覺到自己的靜態引用類型參數占用內存空間比較大,并且使用完畢后不再使用,則可以立刻將其賦值為null。這也許并不必要,但這絕對是一個好習慣。試想一下在一個大系統中,那些時不時在類型中出現的靜態變量吧,它們就那樣靜靜地呆在內存里,一旦被創建,就永遠不離開,越來越多,越來越多。

3
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()