Search code examples
c#garbage-collectionidisposablefinalizerunmanagedresources

Does GC.SupressFinalizer() prevent GC from collecting the managed resources?


If Finalizer (destructor) is implemented in a class and GC.SupressFinalizer() is called from the overriden Dispose() method will the Garbage Collector still take care of any managed resources the instances of that class might have? What confuses me is Microsofts documentation on this matter. For example the implemenation of IDisposable pattern in the virtual Dispose method takes care of the managed resources as well as the unmanaged. Why is that being done if the GC is taking care of the managed resources by default? If I define a class like this:

public class Car
{
    string plateNum;
}

and use this type as a filed in a class that also deals with unmanaged resources, according to the Microsofts documentation, the proper way to handle the disposal of the objects would be to call Dispose on the Car as well. For one to do so Car has to implement the IDisposable interface. Car is only dealing with the managed resources, there is no reason for doing so in the Car class, I have no idea what Dispose() would even do there, maybe give null to the plateNum? Also why would anyone want to implement IDisposable on the class that deals with the managed resources only? Having that in mind, why is there a section in the code in the virtual Dispose() method (in the example in the MS documentation) in which managed resources are disposed?

protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;
            }
        }

The only reason that I can think of is that GC.SupressFinalize(Object) is telling to the GC that it doesn't need to take care of anything related to the object argument that is given. But this shouldn't be the case because implementing the finalizer should only mean that the object that implements it should be put on the Finalizer queue only after the object has been dealt with by the GC because the Finalizer method, that is explicitly implemented by the user, should be called. Also if a finalizer is defined does that change the way the GC collects the managed resources that the instance contains or does it just mean that additional code contained in the finalizer will be executed?


Solution

  • There are many Q+A about this already on SO so I'll give you a very practical answer: You won't ever need to write a finalizer.

    Summary: In the rare case that you have an unmanaged resource, use the SafeHandle class to make it a managed resource.

    When you inspect the full pattern carefully you can see that without unmanaged resources the destructor (aka the Finalizer, ~MyClass() {}) code path does exactly nothing.

    And actually having that Finalizer without SuppressFinalize() is very expensive, it will delay the cleanup of your object to the next GC. Promoting its data to gen 1.

    The remaining reason for the full pattern with virtual void Dispose(bool) is inheritance. A resource holding class should almost never need that. So make it sealed and all you need (want) is:

    public sealed MyClass : IDisposable
    {
       private SomeResource _myResource;
    
       public void Dispose()
       {
          _myResource?.Dispose();
       }
    }
    

    And when you need inheritance then this is the official pattern.