with/without SuppressFinalize in Dispose

Assuming the following:

  • A class has managed only members.
  • Some members implement IDisposable .
  • The class is sealed - a class can't derive from and add unmanaged resources.
  • The object is used inside a using statement - ie Dispose() is called when done.
  • There are 3 possible implementations of IDisposable for this class:

  • Minimal Dispose method that calls Dispose() on IDisposable members - NO finalizer .
  • The standard IDisposable implementation with Finalizer BUT missing the usual GC.SuppressFinalize(this) call in Dispose() .
  • The full standard IDisposable implementation with Finalizer (and with GC.SuppressFinalize(this) call in Dispose() ).
  • Are the following statements correct? Have I understood this correctly?

  • Case A. is has slightly less overhead than B. and C. because the object does not have a finalizer so it does not go in the GCs finalization queue - because of that the GC can clean this object early on in collection - no overhead.
  • Case B. the object has a finalizer so will end up in the GCs finalizer queue and the finalizer will get call (because it wasn't suppressed) - the finalizer calls dispose which does nothing because its been called already. This incurs small overhead of object being in finalizer queue and the very small overhead of finalizer call.
  • Case C. the object has a finalizer so will still end up in the GCs finalizer queue. Because the dispose and hence SuppressFinalize has been called the finalizer won't run. This case still incurs small overhead of the object going in the finalizer queue but the finalizer doesn't actually run.
  • The key point here that it is tempting to think that "I've avoided the finalizer overhead by calling SuppressFinalize " - but I think ( and would like to clarify ) that that is incorrect. The overhead of the object being in the finalizer queue IS still incurred - all you are avoiding is the actual finalizer call - which in the common case is just the "I'm disposed already do nothing".

    Note: Here by "Full standard IDisposable implementation" I mean the standard implementation that is designed to cover both the unmanaged and managed resources case (note here we only have managed object members).

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    private bool _disposed;
    protected virtual void Dispose(bool disposing) {
    if (_disposed)
        return;
        if (disposing) {
            // dispose managed members...
        }
        _disposed = true;
    }
    
    ~AXCProcessingInputs() {
        Dispose(false);
    }
    

    Different versions of the .NET GC may do things differently, but from my understanding any object with a Finalize method will be added to the "finalizer queue" (list of objects which have requested notification if abandoned), and will remain in that queue as long as it exists. The methods to unregister and reregister for finalization (which IMHO should have been protected members of Object ) set or clear a flag in the object header which controls whether the object should be moved to the "freachable queue" (list of objects whose finalize method should run as soon as practical) if it is found to be abandoned, but will not cause the object to be added or removed from the finalizer queue.

    Every instance of every type that overrides Finalize will thus impose a small but non-zero overhead to every garbage-collection cycle in which it takes part, as long as it exists. Calling SuppressFinalize on an object before abandoning it will prevent it from being moved to the freachable queue, but won't eliminate the overhead resulting from its having been in the finalizable queue throughout its existence.

    I would suggest that no public-facing objects should ever implement Finalize . There are some legitimate uses for Finalize methods, but classes that override it should avoid holding references to anything not needed for finalization (somewhat annoyingly, if a finalizable object holds the only reference to a weak reference, the weak reference may be invalidated before the finalizer runs, even if the weak reference's target is still alive).


    You should only include a finalizer on an object that needs to clean up unmanaged resources. Since you only have managed members, you don't need a finalizer - the finalizer will run on the members themselves if they have a finalizer and GC.SuppressFinalize() is not called for them.

    The goal of the finalizer is to clean up unmanaged resources along with their managed wrappers whenever the GC feels like it, while the goal of the Dispose pattern is to clean up any type of resource at a specific moment.

    No one should think "Ive avoided the finalizer overhead by calling SuppressFinalize" - instead they should think "I've cleaned up my unmanaged resources, there is no need for the finalizer to run".

    About the numbered questions:

  • Yes, having a finalizer run will result in some overhead when it is collected
  • Yes, calling Dispose() on an already disposed object should be ignored
  • These classes are added to the finalization queue when an instance is created, but not to the freachable queue when the GC attemots to collect it - it wouldn't make sense to queue an object only to ignore it later. See also this link.
  • 链接地址: http://www.djcxy.com/p/54044.html

    上一篇: 如何迭代Python 3中的Unicode字符?

    下一篇: 在Dispose中使用/不使用SuppressFinalize