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

  當你用Dispose方法釋放未託管對象的時候,應該調用GC.SuppressFinalize。如果對象正在終結隊列(finalization queue),GC.SuppressFinalize會阻止GC調用Finalize方法。因爲Finalize方法的調用會犧牲部分性能。如果你的Dispose方法已經對委託管資源作了清理,就沒必要讓GC再調用對象的Finalize方法(MSDN)。附上MSDN的代碼,大家可以參考.

public class BaseResource : IDisposable

{

    // 指向外部非託管資源

    private IntPtr handle;

    // 此類使用的其它託管資源.

    private Component Components;

    // 跟蹤是否調用.Dispose方法,標識位,控制垃圾收集器的行爲

    private bool disposed = false; 

    // 構造函數

    public BaseResource()

    {// Insert appropriate constructor code here. } 

    // 實現接口IDisposable.不能聲明爲虛方法virtual.子類不能重寫這個方法.

    public void Dispose()

    {

        Dispose(true);

        // 離開終結隊列Finalization queue,設置對象的阻止終結器代碼

        GC.SuppressFinalize(this);

    } 

    // Dispose(bool disposing) 執行分兩種不同的情況.

    // 如果disposing 等於 true, 方法已經被調用

    // 或者間接被用戶代碼調用. 託管和非託管的代碼都能被釋放

    // 如果disposing 等於false, 方法已經被終結器 finalizer 從內部調用過,

    // 你就不能在引用其他對象,只有非託管資源可以被釋放。

    protected virtual void Dispose(bool disposing)

    {

        // 檢查Dispose 是否被調用過.

        if (!this.disposed)

        {

            // 如果等於true, 釋放所有託管和非託管資源 

            if (disposing)

            {

                // 釋放託管資源.

                Components.Dispose();

            }

            // 釋放非託管資源,如果disposing爲 false, // 只會執行下面的代碼.

            CloseHandle(handle);

            handle = IntPtr.Zero;

            // 注意這裏是非線程安全的.

            // 在託管資源釋放以後可以啓動其它線程銷燬對象,

            // 但是在disposed標記設置爲true前

            // 如果線程安全是必須的,客戶端必須實現。 

        }

        disposed = true;

    }

    // 使用interop 調用方法 

    // 清除非託管資源.

    [System.Runtime.InteropServices.DllImport("Kernel32")]

    private extern static Boolean CloseHandle(IntPtr handle); 

    // 使用C# 析構函數來實現終結器代碼

    // 這個只在Dispose方法沒被調用的前提下,才能調用執行。

    // 如果你給基類終結的機會.

    // 不要給子類提供析構函數.

    ~BaseResource()

    {

        // 不要重複創建清理的代碼.

        // 基於可靠性和可維護性考慮,調用Dispose(false) 是最佳的方式

        Dispose(false);

    } 

    // 允許你多次調用Dispose方法,

    // 但是會拋出異常如果對象已經釋放。

    // 不論你什麼時間處理對象都會覈查對象的是否釋放, 

    // check to see if it has been disposed.

    public void DoSomething()

    {

        if (this.disposed)

        {

            throw new ObjectDisposedException();

        }

    } 

    // 不要設置方法爲virtual.

    // 繼承類不允許重寫這個方法

    public void Close()

    {

        // 無參數調用Dispose參數.

        Dispose();

    } 

    public static void Main()

    {

        // Insert code here to create

        // and use a BaseResource object.

    }

}

GC.Collect() 方法

作用:強制進行垃圾回收。

GC的方法:

名稱

說明

Collect()

強制對所有代進行即時垃圾回收。

Collect(Int32)

強制對零代到指定代進行即時垃圾回收。

Collect(Int32, GCCollectionMode)

強制在 GCCollectionMode 值所指定的時間對零代到指定代進行垃圾回收。


GC注意事項:

1、只管理內存,非託管資源,如文件句柄,GDI資源,數據庫連接等還需要用戶去管理

2、循環引用,網狀結構等的實現會變得簡單。GC的標誌也壓縮算法能有效的檢測這些關係,並將不再被引用的網狀結構整體刪除。

3、GC通過從程序的根對象開始遍歷來檢測一個對象是否可被其他對象訪問,而不是用類似於COM中的引用計數方法。

4、GC在一個獨立的線程中運行來刪除不再被引用的內存

5、GC每次運行時會壓縮託管堆

6、你必須對非託管資源的釋放負責。可以通過在類型中定義Finalizer來保證資源得到釋放。

7、對象的Finalizer被執行的時間是在對象不再被引用後的某個不確定的時間。注意並非和C++中一樣在對象超出聲明週期時立即執行析構函數

8、Finalizer的使用有性能上的代價。需要Finalization的對象不會立即被清除,而需要先執行Finalizer.Finalizer不是在GC執行的線程被調用。GC把每一個需要執行Finalizer的對象放到一個隊列中去,然後啓動另一個線程來執行所有這些Finalizer.而GC線程繼續去刪除其他待回收的對象。在下一個GC週期,這些執行完Finalizer的對象的內存纔會被回收。

9、.NET GC使用"代"(generations)的概念來優化性能。代幫助GC更迅速的識別那些最可能成爲垃圾的對象。在上次執行完垃圾回收後新創建的對象爲第0代對象。經歷了一次GC週期的對象爲第1代對象。經歷了兩次或更多的GC週期的對象爲第2代對象。代的作用是爲了區分局部變量和需要在應用程序生存週期中一直存活的對象。大部分第0代對象是局部變量。成員變量和全局變量很快變成第1代對象並最終成爲第2代對象。

10、GC對不同代的對象執行不同的檢查策略以優化性能。每個GC週期都會檢查第0代對象。大約1/10的GC週期檢查第0代和第1代對象。大約1/100的GC週期檢查所有的對象。重新思考Finalization的代價:需要Finalization的對象可能比不需要Finalization在內存中停留額外9個GC週期。如果此時它還沒有被Finalize,就變成第2代對象,從而在內存中停留更長時間。

 

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