4

Garbage Collection can become a time consuming process. In this regard, the GC tends to work only when it has to. If an object has been disposed, to help save time and aid the GC, should the GC's finalizer be suppressed?

using(var sO = new someObject())
{
 //work with object
}

public class someObject : IDisposable
{
  internal Stream someResource;
  internal Context someContext;

  public void Dispose()
  {
   someContext.Dispose();
   someResource.Dispose();
   //A: does suppressing the GC finalizer here save time for the GC?
   //B: can this cause memory leaks?
   //GC.SuppressFinalize(this);
  }
}
Charles
  • 50,943
  • 13
  • 104
  • 142
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • 1
    possible duplicate of [When should I use GC.SuppressFinalize()?](http://stackoverflow.com/questions/151051/when-should-i-use-gc-suppressfinalize) – Grant Thomas Mar 19 '12 at 23:17
  • @Mr.Disappointment - That is a nice thread, but it lacks any coverage of time. I am curious if it is a significant savings of time to be calling GC.SupressFinalize. – Travis J Mar 19 '12 at 23:23

3 Answers3

4

To clear up some confusion:

  • You only need a finalizer if you need to clean up unmanaged resources in some special way.

  • If you have no unmanaged resources, then you don't need a finalizer.

  • If you have a finalizer, then you should definitely implement IDisposable.

  • If you have a finalizer, then you should call GC.SuppressFinalize in Dispose, because the finalizer doesn't need to be called if you've already cleaned up after yourself.

  • You only need to call GC.SuppressFinalize if you have a finalizer. If you don't, you probably still want to call it anyway as a defensive measure.

does suppressing the GC finalizer here save time for the GC?

If there are unmanaged resources and your object has a finalizer, then yes; your object might live a long time on the finalization queue if you don't suppress this. The resources you dispose of may also take a long time to finalize (closing an expensive I/O channel, for example).

If you don't have a finalizer, you're saying that you don't have anything unmanaged to clean up. You should still call SuppressFinalize anyway as a defensive measure.

From the FxCop rules:

"Failure to suppress finalization degrades performance and provides no benefits."


For your other question,

can this cause memory leaks?

In this example, yes, if any of the resources are unmanaged. If your object is terminated in some unusual way before it has a chance to invoke Dispose, unmanaged resources won't be properly freed. Consider what happens if someResource is unmanaged and throws an exception while you're using a someObject instance, for example.

If your object holds onto unmanaged resources and you need to guarantee those resources are cleaned up, you need a finalizer too (which would be named ~someObject in your case).

John Feminella
  • 303,634
  • 46
  • 339
  • 357
  • So suppressing finalization improves performance and provides benefits; however, when suppressing finalization a finalizer should be included as a backup in case something goes wrong. Sorry to re-iterate, but is that correct? – Travis J Mar 19 '12 at 23:29
  • No, other way around. If you need a finalizer, then you need to have a GC.SuppressFinalize. Requiring a finalizer is rare; you only need it for special, unmanaged resources that can't clean up after themselves. – John Feminella Mar 19 '12 at 23:34
  • I updated my answer with some rules of thumb. Hopefully that will clear up the situation for you. – John Feminella Mar 19 '12 at 23:39
3

If you actively Dispose an object then yes you should suppress the finalization. There is no need for the object to be finalized if you actively / deterministically release all of the resources. Letting it hang around for the finalizer is just wasting time later on.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Is the time wasted significant, or is it going to be minute compared to other operations the GC is doing? – Travis J Mar 19 '12 at 23:24
  • @TravisJ the waste of a single object is likely insignificant but this can definitely add up in certain scenarios. There is definitely no reason not to do it :) – JaredPar Mar 19 '12 at 23:32
  • 1
    Just to add: Additionally the memory for the object will not be reclaimed until the finalizer has run. – Brian Rasmussen Mar 19 '12 at 23:35
  • @JaredPar - MSDN states "Objects that implement the IDisposable interface can call this method from the IDisposable.Dispose method to prevent the garbage collector from calling Object.Finalize on an object that does not require it." However, what if there is no defined finalizer as in the case of this example? – Travis J Mar 19 '12 at 23:49
  • @TravisJ: If an object does not override `Object.Finalize`, calling `GC.SuppressFinalize()` will have no effect. In such circumstances, however, the call will be relatively cheap and may thus be considered harmless. – supercat Apr 30 '12 at 19:01
  • 1
    In some cases, a bigger issue than the time required to perform the finalizer is the fact every object with a finalizer, *and every object to which such an object holds a direct or indirect reference*, must be kept in memory until the completion or suppression of the finalizer. Because .net uses a generational garbage collector, failure to suppress a finalizer can result in objects being kept in memory a lot longer than they should. – supercat Sep 28 '12 at 15:42
1

One point not yet mentioned: an object need only be considered "live" if there is some execution path via which code might need to access its fields or its object header (a data structure that holds information about the object's type, whether it's been used as a monitor lock, etc.) If an object field holds an unmanaged handle and the last thing the Dispose method does is to close the handle, it's possible that the object may become eligible for finalization while the Dispose method is running. Oops. Not only will the call to GC.SuppressFinalize serve to prevent the finalizer from getting enqueued after it executes; its placement as the last thing Dispose does will prevent the object from becoming eligible for finalization before it executes.

supercat
  • 77,689
  • 9
  • 166
  • 211