Search code examples
c#garbage-collectionidisposablefinalizer

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


So, the default dispose pattern implementation looks like this:

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);
   }
}

It's said that:

If the method call comes from a finalizer (that is, if disposing is false), only the code that frees unmanaged resources executes. Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling this Dispose overload with a value of false prevents the finalizer from trying to release managed resources that may have already been reclaimed.

The question is: why is it supposed that the objects that are referenced by the object of SomeClass may already have been freed and we shouldn't try to dispose them when the method is called from the finalizer? If those objects are still referenced by our SomeClass object they cannot be freed, isn't it true? It's said that:

Those with pending (unrun) finalizers are kept alive (for now) and are put onto a special queue. [...] Prior to each object’s finalizer running, it’s still very much alive — that queue acts as a root object.

So, again, our SomeClass object is referenced by this queue (which is the same as to be referenced by a root). And other objects the SomeClass object has references to should be alive as well (as they are rooted through the SomeClass object). Then why and how they might have been freed by the time the SomeClass finalizer is called?


Solution

  • Konrad Kokosa has an impressive explanation on his book Pro .NET Memory Management. (emphasis added)

    During GC, at the end of Mark phase, GC checks the finalization queue to see if any of the finalizable objects are dead. If they are some, they cannot be yet delete because their finalizers will need to be executed. Hence, such object is moved to yet another queue called fReachable queue. Its name comes from the fact that it represents finalization reachable objects - the ones that are now reachable only because of finalization. If there are any such objects found, GC indicates to the dedicated finalizer thread there’s work to do.

    Finalization thread is yet another thread created by the.NET runtime. It removes objects from the fReachable queue one by one and calls their finalizers. This happens after GC resumes managed threads because finalizer code may need to allocate objects. Since the only root to this object is removed from the fReachable queue, the next GC that condemns the generation this object is in will find it to be unreachable and reclaim it.

    Moreover, fReachable queue is treated as a root considered during Mark phase because the finalizer thread may not be fast enough to process all objects from it between GCs. This exposes the finalizable objects more to a Mid-life crisis - they may stay in fReachable queue for a while consuming generation 2 just because of pending finalization.

    I think the key here is:

    fReachable queue is treated as a root considered during Mark phase because the finalizer thread may not be fast enough to process all objects from it between GCs.