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

 在標準的Dispose模式中(見前一篇博客C#中標準Dispose模式的實現」),提到了須要及時釋放資源,卻並無進一步細說讓引用等於null是否有必要。 html

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

   
   
   
   
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以後被調用;spa

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

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

上面的兩個方法中各自的局部變量,在代碼運行過程當中會在內存中各自建立一個「根」.在一次垃圾回收中,垃圾回收器會沿着線程棧上行檢查「根」。檢查到方法內的「根」時,若是發現沒有任何一個地方引用了局部變量,則不論是否爲變量賦值爲null,都意味着該「根」已經被中止掉。而後垃圾回收器發現該根的引用爲空,同時標記該根可被釋放,這也表示着Simple類型對象所佔用的內存空間可被釋放。因此,在上面的這個例子中,爲s指定爲null絲毫沒有意義(方法的參數變量也是這種狀況)。code

更進一步的事實是,JIT編譯器是一個通過優化的編譯器,不管咱們是否在方法內部爲局部變量賦值爲null,該語句都會被忽略掉orm

s = null;

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

正式因爲上面這樣的分析,不少人認爲爲對象賦值爲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。這也許並沒必要要,但這絕對是一個好習慣。 試想一下在一個大系統中,那些時不時在類型中出現的靜態變量吧,它們就那樣靜靜地呆在內存裏,一旦被建立,就永遠不離開,愈來愈多,愈來愈多……
相關文章
相關標籤/搜索