註:這裡的文章從"Zendy---勿在浮沙築高台---"複製,目的是讓我有一個比較充分的對這個問題的認識.
文章標題:對.Net 記憶體回收的C#編程相關方面(Finalize 和Dispose(bool disposing)和 Dispose())的一些理解體會
原文章地址:http://www.cnblogs.com/caomao/archive/2006/10/03/152505.html
Finalize 和Dispose(bool disposing)和 Dispose() 的相同點:
這三者都是為了釋放非託管資源服務的.
Finalize 和 Dispose() 和Dispose(bool disposing)的不同點:
Finalize是CRL提供的一個機制, 它保證如果一個類實現了Finalize方法,那麼當該類對象被記憶體回收時,記憶體回收行程會調用Finalize方法.而該類的開發人員就必須在Finalize方法中處理 非託管資源的釋放. 但是什麼時候會調用Finalize由記憶體回收行程決定,該類對象的使用者(客戶)無法控制.從而無法及時釋放掉寶貴的非託管資源.由於非託管資源是比較寶貴了,所以這樣會降低效能.
Dispose(bool disposing)不是CRL提供的一個機制, 而僅僅是一個設計模式(作為一個IDisposable介面的方法),它的目的是讓供類對象的使用者(客戶)在使用完類對象後,可以及時手動調用非託管資源的釋放,無需等到該類對象被記憶體回收那個時間點.這樣類的開發人員就只需把原先寫在Finalize的釋放非託管資源的代碼,移植到Dispose(bool disposing)中. 而在Finalize中只要簡單的調用 "Dispose(false)"(為什麼傳遞false後面解釋)就可以了.
這個時候我們可能比較疑惑,為什麼還需要一個Dispose()方法?難道只有一個Dispose(bool disposing)或者只有一個Dispose()不可以嗎?
答案是:
只有一個Dispose()不可以. 為什麼呢?因為如果只有一個Dispose()而沒有Dispose(bool disposing)方法.那麼在處理實現非託管資源釋放的代碼中無法判斷該方法是客戶調用的還是記憶體回收行程通過Finalize調用的.無法實現 判斷如果是客戶手動調用,那麼就不希望記憶體回收行程再調用Finalize()(調用GC.SupperFinalize方法).另一個可能的原因(:我們知道如果是記憶體回收行程通過Finalize調用的,那麼在釋放代碼中我們可能還會引用其他一些託管對象,而此時這些託管對象可能已經被記憶體回收了, 這樣會導致無法預知的執行結果(千萬不要在Finalize中引用其他的託管對象).
所以確實需要一個bool disposing參數, 但是如果只有一個Dispose(bool disposing),那麼對於客戶來說,就有一個很滑稽要求,Dispose(false)已經被Finalize使用了,必須要求客戶以Dispose(true)方式調用,但是誰又能保證客戶不會以Dispose(false)方式調用呢?所以這裡採用了一中設計模式:重載 把Dispose(bool disposing)實現為 protected, 而Dispose()實現為Public,那麼這樣就保證了客戶只能調用Dispose()(內部調用Dispose(true)//說明是客戶的直接調用),客戶無法調用Dispose(bool disposing).
範例如下:
public class BaseResource: IDisposable
{
//解構函式自動產生 Finalize 方法和對基類的 Finalize 方法的調用.預設情況下,一個類是沒有解構函式的,也就是說,對象被記憶體回收時不會被調用Finalize方法
~BaseResource()
{
// 為了保持代碼的可讀性性和可維護性,千萬不要在這裡寫釋放非託管資源的代碼
// 必須以Dispose(false)方式調用,以false告訴Dispose(bool disposing)函數是從記憶體回收行程在調用Finalize時調用的
Dispose(false);
}
// 無法被客戶直接調用
// 如果 disposing 是 true, 那麼這個方法是被客戶直接調用的,那麼託管的,和非託管的資源都可以釋放
// 如果 disposing 是 false, 那麼函數是從記憶體回收行程在調用Finalize時調用的,此時不應當引用其他託管對象所以,只能釋放非託管資源
protected virtual void Dispose(bool disposing)
{
// 那麼這個方法是被客戶直接調用的,那麼託管的,和非託管的資源都可以釋放
if(disposing)
{
// 釋放 託管資源
OtherManagedObject.Dispose();
}
//釋放非託管資源
DoUnManagedObjectDispose();
// 那麼這個方法是被客戶直接調用的,告訴記憶體回收行程從Finalization隊列中清除自己,從而阻止記憶體回收行程調用Finalize方法.
if(disposing)
GC.SuppressFinalize(this);
}
//可以被客戶直接調用
public void Dispose()
{
//必須以Dispose(true)方式調用,以true告訴Dispose(bool disposing)函數是被客戶直接調用的
Dispose(true);
}
}
上面的範例達到的目的:
1/ 如果客戶沒有調用Dispose(),未能及時釋放託管和非託管資源,那麼在記憶體回收時,還有機會執行Finalize(),釋放非託管資源,但是造成了非託管資源的未及時釋放的空閑浪費
2/ 如果客戶調用了Dispose(),就能及時釋放了託管和非託管資源,那麼該對象被記憶體回收時,不回執行Finalize(),提高了非託管資源的使用效率並提升了系統效能
可以參考SqlConnection對象的New, Open, Close(內部調用Dispose())的使用經曆可以加深對他們的理解.謝謝!