.NET Garbage Collection

 

Java也有GC,當然GC也不是Java首創的,可能Sun的出發點就是要把Java搞得open一點,所以很多東西沒有做具體規定,比如GC的算法,還有System.GC()的調用按照Java標準,並不會一定引發一次垃圾回收。之後各路Java虛擬機風起雲涌,所以當年我作爲Java Developer,理直氣壯的沒有關心GC 內部的具體細節,這不是一個好藉口:)

現在在C#上工作,事實上就MS一家的.Net平臺(聽說還有一個什麼Mono),內部原理定義的很明確,容易瞭解清楚,這也是一家獨大的好處:)

有一點要搞清楚,是.NET的GC,不是C#的GC,C#只是產生.NET能夠執行代碼的語言之一。

C#借鑑了很多C++和Java的東西,對於析構函數,表現得很想C++,也是類名前面加一個~符號,英文叫tlide,但是C#編譯器會把這個東西做一些變化,使得表面上很象C++,但骨子裏還是按照.NET的規矩做事。

比如我們寫一個類的析構函數

~Dummy()
{
    //do something here
}

C#編譯器會將其當下面的內容編譯

protected override void Finalize()//連函數名都改了哦 
{ 
    try 
    { 
        //do something here 
    } 
    finally 
    { 
        base.Finalize(); 
    }
} 

用ILDASM.exe可以看到產生的最終IL代碼中,根本就沒有~Dummy這個函數,因爲C#編譯器自動把它變成protected override void Finalize()了。嗯,Java裏面也有一個finalize()函數,似乎就沒有怎麼被用過。

要是手工添加一個protected override void Finalize()函數,編譯器會給一個編譯錯誤:“Do not override object finalize. Instead, provide a destructor.”

如果一個類只涉及內存資源,沒有必要自己定義析構函數,因爲GC能夠在回收對象的時候把內存也回收了;需要自己定義析構函數往往是因爲涉及到外部資源(unmanaged resources),如系統文件、unmanaged code分配的內存,這些東西GC自個搞不定,需要類在Finalize中說明白哪些東西要釋放。

GC有一個Finalization Queue,對於定義了Finalize的類的對象,創建的時候會在Queue裏面掛號,在真正做GC的時候,就對這個Queue裏面引用的對象挨個調用Finalize,然後再回收內存。這樣保證涉及外部資源對象的“析構函數”在內存釋放之前調用,又避免了對不涉及外部資源的類也要調用“析構函數”,所以,不要沒事定義一個空的析構函數,這會導致無謂的Finalization Queue的延長,影響效率。

和Java一樣,理論上我們不要操心何時做GC,但是也提供了強制做GC的API,Java中的System.gc()只是給虛擬機(VM)一個suggestion,VM可做可不做,VM覺得沒必要做我們也末辦法:) C#中可用System命名空間中的GC.Collect()來強制做一次垃圾回收,按照字面的說法,.NET肯定會做一次GC,但是不保證所有的“垃圾”真的會在這次行動中被回收,呵呵,這就和Java差不多了。

由於GC的過程需要中斷所有正在運行的線程,所以不要頻繁的調用GC.Collect(),不然會很影響性能,一般只有確性程序剛剛釋放了一大塊內存的時候才調用它。我只在寫小段test code的時候使用這個API。

還有一個函數GC.WaitForPendingFinalizers(),和GC.Collect()配合使用可以看到明顯效果,因爲GC.Collect只是引發GC,並不等待GC結束。當然,嚴格說來GC.WaitForPendingFinalizers()也不是等到GC完成才返回,它實際上等到Finalization Queue上的Finalizer都被調用過了就返回,這時候內存還沒有回收呢。  

發佈了16 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章