【Java討論】引用類型賦值爲null對加速垃圾回收的做用(轉載)

有一些人認爲等於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;code

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

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

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

q 方法Method2中的對象先被釋放,雖然它在Method1以後被調用;資源

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

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

上面的兩個方法中各自的局部變量,在代碼運行過程當中會在內存中各自建立一個根.在一次垃圾回收中,垃圾回收器會沿着線程棧上行檢查根。檢查到方法內 的根時,若是發現沒有任何一個地方引用了局部變量,則不論是否爲變量賦值爲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。這也許並沒必要要,但這絕對是一個好習慣。試想一下在一個大系統中,那些時不時在類型中出現的靜態變量吧,它們就那樣靜靜地呆在內存裏,一旦被建立,就永遠不離開,愈來愈多,愈來愈多。

相關文章
相關標籤/搜索