C# garbage collection 筆記

2007/06/06
~ 阿亮 ~

最近有點被 C# 的 Garbage collection(GC) 的機制搞得有點昏頭 !!昏頭!! 故查點資料整理一下。

主要參考自 .NET Gotchas  這本書的 Garbage Collection Gotchas 部份:

一、之前在 class 內的物件,一般在 destruct 過程內處理,比如

class SomeClass
{
    private OtherClass ref1;
    public SomeClass(OtherClass givenObject)
    {
        ref1 = givenObject;
    }

    ~SomeClass()
    {
        ref1 = null; // no meaning.
    }
}

在 C# 內,也是有 ~SomeClass 這種 destruct 的寫法,但意義己不同於 C++ 的 destruct,在 C# 內稱之為 Finalize() 的做法,而 destruct 是種 pseudo-destruct,在 compile 後,會將此 pseudo-destruct 編成 Finalize();而這個 Finalize() 並不會在於使用者將 class SomeClass 宣告成 null 後馬上被進行(C++ 的做法),而是在 GC 在一段時間後決定回收此 class 的資源時,才會去進行 Finalize()。

回到上述的問題,可能在 SomeClass 被處理後,隔好久一段時間後 GC 才處理 ~SomeClass(),此時 ref1 的實際 giveObject 物件可能在別處早就被「處理」成 null 了;另外,在 SomeClass 被處理後,GC 會去除此物件的標記,而變為 inaccessible,當然在內的 ref1 也會變成 inaccessible,所以,做 ref1 = null; 這個動作是沒有意義的。呵~若熟 C++ 的人,一定覺得這個蠻怪的~

所以,不要將 C++ destruct 的觀念用在這裡,用起來一定會覺得很怪,特別在 Trace 每個 class 使用記憶體的情形下 :)

在 Finalize() 內不用特別針對 class 內物件設定 ref1 = null 的情形,同理,由於各 class 進行 GC 的順序也不一定,所以,也不要在 Finalize() 內對其他物件做 method 的動作,比如 ref1.close(),在某些情形下,會造成 deadlock.

Finalize() 屬 protected overridable,所以,在內不應呼叫 base.Finalize().

二、GC 的機制讓使用者不用管 managed resource 的管理(即 .NET 的部份),但 unmanaged resource 的部份就必需馬上處理了,比如 .NET 物件有去調用 COM 元件,而 COM 元件即屬於 unmanaged resource,GC 並不會幫忙處理此類的資源。所以,在 .NET 內提供 IDisposable 的介面,讓 .NET 元件內,提供 Dispose() 函數來處理 unmanaged resource 的釋放,當您想讓你的程式碼決定什麼時候釋放,可以用之。

//Wrapper.cs
using System;
using ACOMCompLib;
using System.Runtime.InteropServices.Marshal;

public class Wrapper : IDisposable
{
     IMyComp comp = new MyCompClass();

     public int doSomething()
     {
         int result;
         comp.doSomething(out result);
         return result;
     }

     ~Wrapper()
     {
         ReleaseComObject(comp);
     }

     #region IDisposable Members

public void Dispose()
     {
         ReleaseComObject(comp);
     }
}

其中,ReleaseComObject 屬於 System.Runtime.InteropServices.Marshal 下的 Method,用來釋放 COM 元件的資源,由於相對於 .NET 元件,可能 COM 元件佔的資源會很大,不能等到 GC 進行 Finalize() 時才釋放該 COM 元件的資源,所以, .NET 提供 Dispose() 讓使用者在於迴圈多次使用時,可以決定什麼時候釋放。

for(int i = 0; i < iterations; i++)
{
  Wrapper theWrapper = null;
  try
  {
     theWrapper = new Wrapper();
     result = theWrapper.doSomething();
   }
   finally
   {
        theWrapper.Dispose();
    }
}

回到前面所提到的 Finalize(),其實做的事情就和 Dispose() 一樣,都在處理 unmanaged resource 為主,但 Finalize() 則交給 GC 安排時間去處理, Dispose() 則讓使用者使用的時機。

可在 Dispose() 結束前,呼叫 GC.SuppressFinalize(); 則 GC 則不會再處理此 class 的 Finalize() 了。

bool disposing=true/false 都表示有做 resource cleanup 的動作,唯 disposing=true 表示做了 IDisposable.Dispose(),而 disposing=false 表示做了 Finalize()。

三、可讓 Dispose() 處理 managed and unmanaged resource 管理,而 Finalize() 處理 unmanaged resource,所以,有類似下述的設計

protected virtual void Dispose(bool disposing)
{
        if (!disposed)
        {
            if (disposing)
            {
                 // Code to clean up managed resources
            }
                 // Code to clean up unmanaged resources
        }

        disposed = true;
    }

    public void Dispose()

{
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~Base()
    {
        Dispose(false);
    }
}

2008.03 補充

  • 由於 Finalizer 的執行的時間和順序,「很自然地」屬於不可預期,而且有可能某個 instance 都不會被呼叫到 所以,對於 unmanaged resource 的釋放不要依賴 GC 來處理,特別是 file handle 或者 database connection.
  • 不要讓不同 Thread 同時使用 Dispose(),會有不可預期的情形。
  • 所以,除非有用到 unmanaged resource 並需要釋放,才用 Dispose/Finaliz。

其他 References:

唉~ 雖然整理出來,好像不是我目前所遇到的問題? Orz



2 Responses to “C# garbage collection 筆記”

  • 阿亮  說:

    ExecuteSelectCommand 我沒用過,略查一下是 COM 元件?而且我猜和 SQL 有關的東西,那可能要檢查一下是否 connection 沒有 close 掉? ?_?

  • Elsa  說:

    請教一下,因為我的 Web 常出現 Outofmemory….
    是否因為程式有太多 Session,或 DataSet、DataTable 之類的沒有釋掉掉的原因?
    在 C#程式中,利用 Dispose() 是否真的釋放掉資源?或者有什麼好的寫法?

    Ex: DataTable lDT=this.ExecuteSelectCommand(pSQL).Tables[0];
    在資料取得之後寫 lDT.Dispose();

站內搜尋



本站其他服務

本站其他軟體



  • 藝文快訊

    讓你可以輕鬆追蹤含有您想要關注關鍵詞的任何藝文活動訊息,只要有最新的資訊,「藝文快訊」即會推播通知給你.


  • 照片去背(PhotoEraser)

    一款方便移除背景的工具,產生透明背景圖可以存回原本相簿,也可分享到其他 App 使用.


  • 下一台單車(NextBike)

    打開定位即搜尋附近二十點自行車站點,不塞滿全部站點資料到整個地圖上,所以畫面簡潔方便看清楚目前所在地,若需要搜尋地圖其他位置附近站點,再點擊地圖即可。


  • 下一班公車(nextBus)

    這個 app 只要開啟後,就根據定位幫你過濾出附近站牌的時刻表,以及提供相關公車預計到站的時間,方便您在很快時間內確定要坐的哪一班公車


  • 下一班火車 (nextRail)

    這個 app 只要開啟後,就根據定位幫你過濾出最近火車站的時刻表,不用再按任何按鈕了,方便您在很快時間內確定要坐的哪一班火車


  • 姓名筆畫吉凶查詢系統

    這是一個提供中文字康熙筆畫的小軟體,並根據農民曆計算每個名字或公司名的總筆畫以及最後的吉凶數,共有四種模式