步步爲營 C# 技術漫談 四、垃圾回收機制(GC) 中

 

三、Finalization Queue和Freachable Queue

這兩個隊列和.net對象所提供的Finalize方法有關。這兩個隊列並不用於存儲真正的對象,而是存儲一組指向對象的指針。當程序中使用了new操作符在Managed Heap上分配空間時,GC會對其進行分析,如果該對象含有Finalize方法則在Finalization Queue中添加一個指向該對象的指針。在GC被啓動以後,經過Mark階段分辨出哪些是垃圾。再在垃圾中搜索,如果發現垃圾中有被Finalization Queue中的指針所指向的對象,則將這個對象從垃圾中分離出來,並將指向它的指針移動到Freachable Queue中。這個過程被稱爲是對象的復生(Resurrection),本來死去的對象就這樣被救活了。爲什麼要救活它呢?因爲這個對象的Finalize方法還沒有被執行,所以不能讓它死去。Freachable Queue平時不做什麼事,但是一旦裏面被添加了指針之後,它就會去觸發所指對象的Finalize方法執行,之後將這個指針從隊列中剔除,這是對象就可以安靜的死去了。.net framework的System.GC類提供了控制Finalize的兩個方法,ReRegisterForFinalize和SuppressFinalize。前者是請求系統完成對象的Finalize方法,後者是請求系統不要完成對象的Finalize方法。ReRegisterForFinalize方法其實就是將指向對象的指針重新添加到Finalization Queue中。這就出現了一個很有趣的現象,因爲在Finalization Queue中的對象可以復生,如果在對象的Finalize方法中調用ReRegisterForFinalize方法,這樣就形成了一個在堆上永遠不會死去的對象,像鳳凰涅槃一樣每次死的時候都可以復生。

 

託管資源:

Net中的所有類型都是(直接或間接)從System.Object類型派生的。

CTS中的類型被分成兩大類——引用類型(reference type,又叫託管類型[managed type]),分配在內存堆上,值類型(value type)。值類型分配在堆棧上。如圖

alt

     值類型在棧裏,先進後出,值類型變量的生命有先後順序,這個確保了值類型變量在推出作用域以前會釋放資源。比引用類型更簡單和高效。堆棧是從高地址往低地址分配內存。

     引用類型分配在託管堆(Managed Heap)上,聲明一個變量在棧上保存,當使用new創建對象時,會把對象的地址存儲在這個變量裏。託管堆相反,從低地址往高地址分配內存,如圖

alt

.net中超過80%的資源都是託管資源。

 

非託管資源:

ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,Timer,Tooltip ,文件句柄,GDI資源,數據庫連接等等資源。可能在使用的時候很多都沒有注意到!

 

.NET的GC機制有這樣兩個問題:

首先,GC並不是能釋放所有的資源。它不能自動釋放非託管資源。

第二,GC並不是實時性的,這將會造成系統性能上的瓶頸和不確定性。

GC並不是實時性的,這會造成系統性能上的瓶頸和不確定性。所以有了IDisposable接口,IDisposable接口定義了Dispose方法,這個方法用來供程序員顯式調用以釋放非託管資源。使用using 語句可以簡化資源管理。

示例

/// <summary>
/// 執行SQL語句,返回影響的記錄數
/// </summary>
/// <param name="SQLString">SQL語句</param>
/// <returns>影響的記錄數</returns>
public static int ExecuteSql(string SQLString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        using (SqlCommand cmd = new SqlCommand(SQLString, connection))
        {
            try
            {
                connection.Open();
                int rows = cmd.ExecuteNonQuery();
                return rows;
            }
            catch (System.Data.SqlClient.SqlException e)
            {
                connection.Close();
                throw e;
            }
            finally
            {
                cmd.Dispose();
                connection.Close();
            }
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章