with/without SuppressFinalize in Dispose
Assuming the following:
IDisposable
. sealed
- a class can't derive from and add unmanaged resources. using
statement - ie Dispose()
is called when done. There are 3 possible implementations of IDisposable
for this class:
Dispose
method that calls Dispose()
on IDisposable
members - NO finalizer . IDisposable
implementation with Finalizer BUT missing the usual GC.SuppressFinalize(this)
call in Dispose()
. IDisposable
implementation with Finalizer (and with GC.SuppressFinalize(this)
call in Dispose()
). Are the following statements correct? Have I understood this correctly?
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:
Dispose()
on an already disposed object should be ignored