對.Net 垃圾回收的一些理解體會

Finalize 和Dispose(bool disposing)和 Dispose() 的相同點:

這三者都是爲了釋放非託管資源服務的.

Finalize 和 Dispose() 和Dispose(bool disposing)的不同點:

  1. Finalize是CRL提供的一個機制, 它保證如果一個類實現了Finalize方法,那麼當該類對象被垃圾回收時,垃圾回收器會調用Finalize方法.而該類的開發者就必須在 Finalize方法中處理 非託管資源的釋放. 但是什麼時候會調用Finalize由垃圾回收器決定,該類對象的使用者(客戶)無法控制.從而無法及時釋放掉寶貴的非託管資源.由於非託管資源是比較寶 貴了,所以這樣會降低性能.
  2. 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())的使用經歷可以加深對他們的理解.謝謝!

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