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

  當你用Dispose方法釋放未託管對象的時候,應該調用GC.SuppressFinalize。若是對象正在終結隊列(finalization queue),GC.SuppressFinalize會阻止GC調用Finalize方法。由於Finalize方法的調用會犧牲部分性能。若是你的Dispose方法已經對委託管資源做了清理,就不必讓GC再調用對象的Finalize方法(MSDN)。附上MSDN的代碼,你們能夠參考.算法

public class BaseResource : IDisposable數據庫

{安全

    // 指向外部非託管資源app

    private IntPtr handle;ide

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

    private Component Components;性能

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

    private bool disposed = false; this

    // 構造函數spa

    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注意事項:

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

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

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

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

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

六、你必須對非託管資源的釋放負責。能夠經過在類型中定義Finalizer來保證資源獲得釋放。

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

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

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

十、GC對不一樣代的對象執行不一樣的檢查策略以優化性能。每一個GC週期都會檢查第0代對象。大約1/10的GC週期檢查第0代和第1代對象。大約1/100的GC週期檢查全部的對象。從新思考Finalization的代價:須要Finalization的對象可能比不須要Finalization在內存中停留額外9個GC週期。若是此時它尚未被Finalize,就變成第2代對象,從而在內存中停留更長時間。

相關文章
相關標籤/搜索