4

Regarding the Microsoft built classes that inherit IDisposable, do I explicitly have to call Dispose to prevent memory leaks?

I understand that it is best practice to call Dispose (or better yet use a using block), however when programming, typically I don't always immediately realise that a class inherits from IDisposable.

I also understand that Microsoft implementation of IDisposable is a bit borked, which is why they created the article explaining the correct usage of IDisposable.

Long story short, in which instances is it okay to forget to call Dispose?

svick
  • 236,525
  • 50
  • 385
  • 514
Matthew
  • 24,703
  • 9
  • 76
  • 110
  • 1
    No, this is not okay. Don't forget. You'll end up like [this guy](http://stackoverflow.com/questions/8836964/gdi-generic-error-memory-leak-using-vb-net). – Cody Gray - on strike Jan 12 '12 at 16:14

7 Answers7

10

There are a couple of issues in the primary question

Do I explicitly have to call Dispose to prevent memory leaks?

Calling Dispose on any type which implements IDisposable is highly recomended and may even be a fundamental part of the types contract. There is almost no good reason to not call Dispose when you are done with the object. An IDisposable object is meant to be disposed.

But will failing to call Dispose create a memory leak? Possibly. It's very dependent on what exactly that object does in it's Dispose method. Many free memory, some unhook from events, others free handles, etc ... It may not leak memory but it will almost certainly have a negative effect on your program

In which instances is it okay to forget to call Dispose?

I'd start with none. The vast majority of objects out there implement IDisposable for good reason. Failing to call Dispose will hurt your program.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • To be clear, I cannot assume that Dispose will eventually be called from the finalizer? – Matthew Jan 12 '12 at 16:22
  • 1
    @Mathew no, not every class which implements `IDisposable` also has a finalizer. They are different concepts which just happen to be very often paired together. Also the capabilities of an object in a finalizer are less than those in an explicit dispose call. Typically dispose is broken up into managed and unmanaged cleanup and managed cleanup cannot be reliably done from a finalizer. – JaredPar Jan 12 '12 at 16:24
  • 6
    @Matthew: Also, you have no guarantee that the finalizer will be called, ever. The object might never be garbage collected. Even if it is dead, the GC could choose to only collect and finalize it when the system starts to run out of memory, and that could be never. – Eric Lippert Jan 12 '12 at 16:38
  • 4
    @Matthew: "A correctly-written program cannot assume that finalizers will ever run." From [Everybody thinks about garbage collection the wrong way](http://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx) by Raymond Chen – Brian Jan 12 '12 at 18:22
4

It depends on two things:

  1. What happens in the Dispose method
  2. Does the finalizer call Dispose

Dispose functionlity
Dispose can do several type of actions, like closing a handle to a resource (like file stream), change the class state and release other components the class itself uses.
In case of resource being released (like file) there's a functionality difference between calling it explicitly and waiting for it to be called during garbage collection (assuming the finalizer calls dispose).
In case there's no state change and only components are released there'll be no memory leak since the object will be freed by the GC later.

Finalizer
In most cases, disposable types call the Dispose method from the finalizer. If this is the case, and assuming the context in which the dispose is called doesn't matter, then there's a high chance that you'll notice no difference if the object will not be disposed explicitly. But, if the Dispose is not called from the finalizer then your code will behave differently.

Bottom line - in most cases, it's better to dispose the object explicitly when you're done with it.

A simple example to where it's better to call Dispose explicitly: Assuming you're using a FileStream to write some content and enable no sharing, then the file is locked by the process until the GC will get the object. The file may also not flush all the content to the file so if the process crashes in some point after the write was over it's not guaranteed that it will actually be saved.

Elisha
  • 23,310
  • 6
  • 60
  • 75
  • Thank you for the clarification about the Finalizer, I was under the assumption that the dispose method eventually gets called by *something* (the GC), and you're saying this isn't the case. – Matthew Jan 12 '12 at 16:27
  • 1
    It's not true that "in most cases, disposable types call the Dispose method from the finalizer", though it is true that somewhere along the chain of disposes calling disposes there's something that will also be handled by the finalizer. An application can starve waiting for that to happen though. – Jon Hanna Jan 12 '12 at 17:39
3

It can be safe to not call Dispose, but the problem is knowing when this is the case.

A good 95% of IEnumerator<T> implementations have a Dispose that's safe to ignore, but the 5% is not just 5% that'll cause a bug, but 5% that'll cause a nasty hard to trace bug. More to the point, code that gets passed an IEnumerator<T> will see both the 95% and the 5% and won't be able to dynamically tell them apart (it's possible to implement the non-generic IEnumerable without implementing IDisposable, and how well that turned out can be guessed at by MS deciding to make IEnumerator<T> inherit from IDisposable!).

Of the rest, maybe there's 3 or 4% of the time it's safe. For now. You don't know which 3% without looking at the code, and even then the contract says you have to call it, so the developer can depend on you doing so if they release a new version where it is important.

In summary, always call Dispose(). (I can think of an exception, but it's frankly too weird to even go into the details of, and it's still safe to call it in that case, just not vital).

On the question of implementing IDisposable yourself, avoid the pattern in that accursed document.

I consider that pattern an anti-pattern. It is a good pattern for implementing both IDisposable.Dispose and a finaliser in a class that holds both managed and unmanaged resources. However holding both managed IDisposable and unmanaged resources is a bad idea in the first place.

Instead:

If you have an unmanaged resource, then don't have any unmanaged resources that implement IDisposable. Now the Dispose(true) and Dispose(false) code paths are the same, so really they can become:

public class HasUnmanaged : IDisposable
{
  IntPtr unmanagedGoo;
  private void CleanUp()
  {
    if(unmanagedGoo != IntPtr.Zero)
    {
      SomeReleasingMethod(unmanagedGoo);
      unmanagedGoo = IntPtr.Zero;
    }
  }
  public void Dispose()
  {
    CleanUp();
    GC.SuppressFinalize(this);
  }
  ~HasUnmanaged()
  {
    CleanUp();
  }
}

If you have managed resources that need to be disposed, then just do that:

public class HasUnmanaged : IDisposable
{
  IDisposable managedGoo;
  public void Dispose()
  {
    if(managedGoo != null)
      managedGoo.Dispose();
  }
}

There, no cryptic "disposing" bool (how can something be called Dispose and take false for something called disposing?) No worrying about finalisers for the 99.99% of the time you won't need them (the second pattern is way more common than the first). All good.

Really need something that has both a managed and an unmanaged resource? No, you don't really, wrap the unmanaged resource in a class of your own that works as a handle to it, and then that handle fits the first pattern above and the main class fits the second.

Only implement the CA10634 pattern when you're forced to because you inherited from a class that did so. Thankfully, most people aren't creating new ones like that any more.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • Although I consider the `bool` parameter of `Dispose(bool)` to be a dummy parameter, the pattern of having a non-virtual method implement an interface by calling a protected virtual method is a good one since it allows a consistent pattern for derived classes to add dispose logic independent of whether their parent implements IDisposable implicitly or explicitly. I dislike the non-virtual function as implemented by Microsoft, however; IMHO, it should use an integer flag with `Interlocked.Exchange`, to provide thread-safe protection against redundant disposal. – supercat Jan 12 '12 at 18:36
  • @supercat it's not as consistent as just a plain virtual `Dispose()`. – Jon Hanna Jan 12 '12 at 20:31
  • Not all classes that implement `IDisposable` have a public `Dispose` method whose semantics are supposed to match those of `IDisposable.Dispose`. – supercat Jan 12 '12 at 20:34
1

It is never OK to forget to call Dispose (or, as you say, better yet use using).

I guess if the goal of your program is to cause unmanaged resource leaks. Then maybe it would be OK.

Domenic
  • 110,262
  • 41
  • 219
  • 271
  • 1
    It wont leak, the Finalizer will do the job. But you can cause unmanaged resource starvation. – Guillaume Jan 12 '12 at 16:11
  • I take your point, but in theory the finalizer might never be called. It's entirely nondeterministic and up to the framework. – Domenic Jan 12 '12 at 16:13
  • (And, of course, that assumes an implementation that calls `Dispose` from the finalizer, which while recommended is not automatic.) – Domenic Jan 12 '12 at 16:19
  • @Guillaume: There are many cases, such as event subscriptions, where finalization really doesn't work as a cleanup mechanism; if an object `George` subscribes to one of object `Bob`'s events and is abandoned, the event subscription will keep `George` from being finalized during `Bob`'s lifetime. If the number of subscribers that may be added and abandoned during `Bob`'s lifetime is unbounded, the memory consumed by the event will likewise be unbounded. – supercat Jan 27 '13 at 20:00
  • @supercat I don't know many "Microsoft built classes" implementing IDisposable for event unsubscription. The primary use of IDisposable is to release unmanaged resources. Anyway, this is dirty and arguable ; you're right always call Dispose :) – Guillaume Jan 28 '13 at 09:01
  • @Guillaume: While there are some rare situations where finalizer-based cleanup is adequate and proper use of `Dispose` would be impractical, I think in many ways .net would have been better if `Dispose` had been more central to the framework, and finalization less so (e.g. instead of having `Finalize` be an overridable method, have a form of `WeakReference` which will fire a delegate when it's invalidated [stuff needed for cleanup must be kept in an object separate from the target of the `WeakReference`, but finalizable objects should only hold what's needed for cleanup anyway). – supercat Jan 28 '13 at 15:57
0

The implementation of IDisposable indicates that a class uses un-managed resources. You should always call Dispose() (or use a using block when possible) when you're sure you're done with the class. Otherwise you are unnecessarily keeping un-managed resources allocated.

In other words, never forget to call Dispose().

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • Maybe I didn't understand your answer, but `IDisposable` doesn't imply at all that a class uses unmanaged resources. Basically, you **should** implement `IDisposable` to manually dispose every of your private member that implements `IDisposable` itself. It doesn't mean those private members are unmanaged resources (often they are instances of built-in .Net managed classes such as Stream...etc.) – ken2k Jan 12 '12 at 16:14
  • @ken2k - This is taken straight from the MSDN docs - "The primary use of this interface (IDisposable) is to release unmanaged resources". If your class is composed of other IDisposable members, then you too are using unmanaged resources (albeit indirectly). – Justin Niessner Jan 12 '12 at 16:29
  • I understand. But I'm not sure it's completely OK to say _My class uses unmanaged resources as one of its private member is a `Stream`_. The whole .Net framework would be unmanaged as well. – ken2k Jan 12 '12 at 16:38
  • @ken2k: Yes it's OK. Having `Stream` in your namespace is not the same as having `Stream` as a class member. Plenty of classes in the .Net framework neither contain `Stream` members nor inherit from `Stream`. – Brian Jan 12 '12 at 18:27
  • I would word it as: "The implementation of IDisposable indicates that a type *might* hold unmanaged resources, so whoever calls their constructor should ensure that `Dispose` will get called; in the absence of any specific contract to the contrary, the stronger implication is that classes which don't implement `IDisposable` don't hold unmanaged resources and do not impose cleanup responsibilities upon their creator. Note that it is acceptable for a class to implement `IDisposable` even if its parent does not, without violating the Liskov Substitutability Principle, because... – supercat Jan 12 '12 at 18:41
  • ...code which calls `new parentClass()` won't unexpectedly receive an `IDisposable`, and thus won't have to worry about cleaning it up, code which calls `new disposableChildClass()` will receive an `IDisposable` and thus know it does have to clean it up, and code which uses a passed-in instance of `baseClass` can expect that the calling code will clean up that instance, so it doesn't have to. Note that factory methods should not return `IDisposable` objects unless the declared return type is an `IDisposable` class, since callers of the factory will not be expecting to do cleanup. – supercat Jan 12 '12 at 18:43
0

Yes, always call dispose. Either explicitly or implicitly (via using). Take, for example, the Timer class. If you do not explicitly stop a timer, and do not dispose it, then it will keep firing until the garbage collector gets around to collecting it. This could actually cause crashes or unexpected behavior.

It's always best to make sure Dispose is called as soon as you are done with it.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
0

Microsoft (probably not officially) says it is ok to not call Dispose in some cases.

Stephen Toub from Microsoft writes (about calling Dispose on Task):

In short, as is typically the case in .NET, dispose aggressively if it's easy and correct to do based on the structure of your code. If you start having to do strange gyrations in order to Dispose (or in the case of Tasks, use additional synchronization to ensure it's safe to dispose, since Dispose may only be used once a task has completed), it's likely better to rely on finalization to take care of things. In the end, it's best to measure, measure, measure to see if you actually have a problem before you go out of your way to make the code less sightly in order to implement clean-up functionality.

[bold emphasize is mine]

Another case is base streams

var inner = new FileStrem(...);
var outer = new StreamReader(inner, Encoding.GetEncoding(1252));
...
outer.Dispose();
inner.Dispose(); -- this will trigger a FxCop performance warning about calling Dispose twice.

(I have turned off this rule)

adrianm
  • 14,468
  • 5
  • 55
  • 102
  • Would it be reasonable for a bitmap factory which produces small bitmaps from strings to keep a `Dictionary` to hold bitmaps, if the expected semantics are that the control which calls a factory will hold a reference to its bitmap until it changes, but one bitmap may be used by many controls? Keeping track of whether any particular bitmap is still needed would be a nuisance, and the fact that `WeakReference` objects are kept would mean that the same bitmap won't get recreated unless or until the old one gets GC'ed. – supercat May 19 '12 at 22:14
  • Depends on why you cache the bitmaps. If you do it because creating them is expensive then it is probably ok. If you cache them to keep the number of bitmap objects down I am not so sure. If the bitmaps live long they probably end up in GC gen 2 which means it might take a long time between the last reference is gone and the finalizer is invoked. No cache + dispose might be a better alternative. – adrianm May 21 '12 at 09:05
  • In this case, the bitmaps represent the contents of disk blocks for a embedded-device simulation; the device in question will frequently have to move blocks around, so it would be very common for e.g. block #893 to hold some particular content, and then for the system to copy that content to block #471 and then erase block #893. The simulator would just see a request to write 4096 bytes to block #471 that happened to match those previously in #893. If a block gets erased when there isn't another block which has the same sequence of bytes, its bitmap will likely never get reused, but... – supercat May 21 '12 at 15:26
  • ...I don't know whether it's better to try to maintain reference counts for how many blocks hold a particular sequence of bytes (and should thus reuse a bitmap) or whether it's better to have each block and the cache hold a `WeakReference` to its bitmap and let the GC figure out which bitmaps are no longer needed. – supercat May 21 '12 at 15:31