簡體   English   中英

C#中的“處置模式”:為什么我們需要“如果(處置)”條件?

[英]A `dispose pattern` in C#: why do we need `if (disposing)` condition?

因此,默認的處置模式實現如下所示:

class SomeClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;

   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }

   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
      }

      // Free any unmanaged objects here.
      disposed = true;
   }

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

據說:

如果方法調用來自終結器(即,如果dispose為false ),則僅執行釋放非托管資源的代碼。 因為未定義垃圾收集器在完成過程中銷毀托管對象的順序,所以使用值為false調用此Dispose重載可防止終結器嘗試釋放可能已經回收的托管資源。

問題是:為什么要假定SomeClass對象所引用的對象可能已被釋放,並且在從終結器調用該方法時,我們不應該嘗試處置它們? 如果這些對象仍被我們的SomeClass對象引用,則無法釋放它們,不是嗎? 據說:

那些具有待定(未運行)終結器的組件將保持活動狀態(暫時)並置於特殊隊列中。 [...]在運行每個對象的終結器之前, 它仍然非常活躍-隊列充當根 對象

因此,同樣,此隊列引用了我們的SomeClass對象(與根引用相同)。 SomeClass對象引用的其他對象也應該是活動的(因為它們是通過SomeClass對象植根的)。 那么,為什么以及如何在調用SomeClass終結器時將它們釋放?

康拉德·科科薩(Konrad Kokosa)對他的Pro .NET內存管理一書有令人印象深刻的解釋。 (強調)

在GC期間,在Mark階段結束時,GC檢查終結隊列,以查看是否有任何終結對象已死。 如果它們是某些,則它們尚不能刪除,因為將需要執行其終結器 因此,此類對象將移動到另一個名為fReachable queue的隊列 它的名字來自於這樣一個事實,即它代表最終確定的可到達對象- 現在僅由於最終確定而可到達的對象。 如果找到任何這樣的對象,GC會向專用的終結器線程指示有工作要做。

終結線程是.NET運行時創建的另一個線程。 它從fReachable隊列中一一刪除對象,並調用它們的終結器 GC恢復托管線程后會發生這種情況,因為終結器代碼可能需要分配對象。 由於從fReachable隊列中刪除了該對象的唯一根,因此下一個譴責此對象所在的世代的GC將發現該對象不可訪問並對其進行回收。

此外,將fReachable隊列視為Mark階段考慮的根,因為終結器線程可能不夠快,無法在GC之間處理來自該終結器線程的所有對象 這使可終結化對象更多地處於中年危機中-由於即將完成的終結處理,它們可能會在fReachable隊列中占用第二代資源一段時間。

我認為這里的關鍵是:

fReachable隊列被視為Mark階段考慮的根,因為終結器線程可能不夠快,無法在GC之間處理來自該終結器線程的所有對象。

.NET中的對象存在,而對它們的任何引用都存在。 最后一個參考文獻一經存在,它們便不復存在。 當對象存在時,將永遠不會回收該對象使用的存儲,但是在回收存儲之前,GC有以下幾項操作:

  1. 有一個特殊的列表,稱為“終結器隊列”,其中包含對已注冊終結器的所有對象的引用。 在確定了Universe中任何其他地方存在的所有其他引用之后,GC將檢查終結器隊列中的所有對象,以查看是否找到了對它們的任何引用。 如果此過程使它找到以前未發現的對象,則將引用復制到另一個列表,稱為“可到達隊列”。 每當可訪問性隊列為非空且沒有終結器運行時,系統將從該隊列中提取引用並對其調用終結器。

  2. GC還將檢查所有弱引用的目標,並使那些尚未被任何實時強引用標識的目標的弱引用無效。

請注意,finalize方法不會“垃圾回收”對象。 取而代之的是,它延長了對象的存在,直到調用了finalize為止, 目的是使其能夠履行對外部實體可能承擔的任何義務 如果那時在宇宙中的任何地方都沒有引用該對象,那么該對象將不復存在。

請注意,兩個帶有終結器的對象可能會相互保持引用。 在這種情況下,未確定其終結器的運行順序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM