12

In the comments to an answer I wrote we had a discussion about memory leaks and IDisposable where we didn't come to any real conclusion.

A class that handles unmanaged resources likely implements IDisposable. If ignore that and neither call Dispose nor wraps the object in a using - will that lead to the unmanaged resource being leaked? Or will it be properly cleaned up when the GC collects the object?

We can assume that the class handling the unmanaged resource has a correct implementation of IDisposable, including finalizer etc.

Community
  • 1
  • 1
Anders Abel
  • 64,109
  • 15
  • 143
  • 213
  • http://www.bluebytesoftware.com/blog/2005/04/08/DGUpdateDisposeFinalizationAndResourceManagement.aspx – Kirk Woll Jul 28 '11 at 20:40
  • 1
    Assuming things are done correctly can be a dangerous thing. But even so, this is related: http://stackoverflow.com/questions/6652044/c-language-garbage-collection-suppressfinalize/6652318#6652318 – Anthony Pegram Jul 28 '11 at 20:41
  • 3
    There are many situations where a correct finalizer _can't_ be written because Finalize is called on a different thread than Dispose would be called on if called normally. – Ed Bayiates Jul 28 '11 at 21:17
  • 1
    @AresAvatar: +1 Indeed, I consider it unfortunate that Microsoft didn't declare that all correctly-implemented events must provide a thread-safe means of unsubscribing, since even if an event subscriber discovers that he's no longer needed (e.g. because an object to which the entities interested in the event hold a strong reference, but the event handler does not, got finalized) he can't unsubscribe the event unless he knows the publisher offers thread-safe unsubscription. – supercat Jul 29 '11 at 00:05

7 Answers7

15

It will not cause a memory leak. In fact, Dispose has absolutely nothing to do with memory management.

It will create a resource-leak. And while the GC will usually clean it up, this could be too infrequent and too late.

Omitting Dispose (using) can slow down or even crash your App. In the case of file resources or Db connections it can even cause problems in other applications.

Henk Holterman
  • 236,989
  • 28
  • 287
  • 464
  • 2
    Ignoring IDisposable may cause a program that should run in a bounded amount of memory (if IDisposable were correctly used) to require an unbounded amount of memory. If needless unbounded growth in a program's memory requirements is regarded as a memory leak, ignoring IDisposable can cause a memory leak. – supercat Jul 29 '11 at 14:59
  • @Supercat: Total balderdash, for the reasons stated above. Maybe you meant "bounded amount of resources" but that's just repeating the answer. – Henk Holterman Jul 29 '11 at 16:40
  • Suppose an IEnumerator subscribes to an INotifyCollectionChangedEvent to allow practical enumeration of a changing collection (a useful paradigm, btw, and one which is supported by the VisualBasic.Collection class). Unless the collection and enumerator both include some complicated logic to ensure finalization (such logic is not easy, and adds considerable overhead) IEnumerators which are not Disposed will have their life extended to that of the collection. Since there is no limit to the number of times a collection may be enumerated... – supercat Jul 29 '11 at 18:32
  • ...there would also be no limit to the number of enumerators that would have to be created and held in memory before any of them could be freed. No matter how much memory a machine had, it would all get used up if a process enumerated the list too many times during the lifetime of the collection. This despite the fact that enumerating the list each time might not add be adding anything to the application's amount of useful state. – supercat Jul 29 '11 at 18:38
  • If an application allocates a 256-megabyte object and holds onto it for no good reason, but won't ever hold references to more than two such objects (the most recently-used one, and a new one being created) that's not a memory leak since the amount of memory wastage, though large, is bounded. If, however, every time the user clicked a button an object was created, subscribed to an event handler in the current window, and abandoned, that would be a memory leak since the amount of memory "simultaneously" wasted would be unbounded. – supercat Jul 29 '11 at 18:43
  • Of course there are possible ways to create memory leaks, event subscription being a notorious example. And yes you can (ab)use Dispose to clean it up, but usually a better design is preferred. It is not part of the main IDisposable pattern. – Henk Holterman Jul 29 '11 at 19:33
  • 1
    What pattern would you suggest for an IEnumerator to clean up its events, if not IDisposable.Dispose? I would suggest that except when such a design would be grossly impractical or impossible, all objects that require explicit cleanup should perform all necessary cleanup when their IDisposable.Dispose method is called. I would consider any design that requires calling some other method to be highly suspect. – supercat Jul 29 '11 at 20:25
9

I will not cause managed memory leaks. It can cause leaks in referenced unmanaged code. But it's worse than that: memory on modern systems is plentiful enough that you can often get by for a while with a bad leak. Witness Mozilla Firefox: it used to (does it still?) leak like a sieve, and millions were happy to use it.

The bigger problem is other resources that may have nothing to do with memory at all. Examples include database connections, system I/O handles, socket handles, file handles, and the like. These are all items where you can easily create denial of service situations on your own system if you aren't careful to use IDisposable properly.

Joel Coehoorn
  • 362,140
  • 107
  • 528
  • 764
  • A program which creates and abandons an unbounded number of event subscribers for a long-lived object will require an unbounded amount of memory to hold those subscribers, even if the number of non-abandoned subscribers at any given time never grows beyond a constant (or, for that matter, one). Such abandoned subscriptions would certainly fulfill my definition of a memory leak (the amount of memory required for a program to handle some input sequence is unbounded with respect to the amount of "useful" state it holds). – supercat Jul 29 '11 at 15:08
4

Just to add a little to Henk and Joel's answers

What you've described happens quite often on DB Connections specifically. Enough that the ADO.NET Performance counter NumberOfReclaimedConnections got added. This counter tracks...

The number of connections that have been reclaimed through garbage collection where Close or Dispose was not called by the application. Not explicitly closing or disposing connections hurts performance.

The performance hit is usually a longer then necessary wait for a connection to be freed up. This can also result in timeouts on the connection, not a memory problem.

Conrad Frix
  • 49,660
  • 12
  • 87
  • 144
2

If an IDisposable object has a finalizer that de-allocates unmanaged memory then the memory will be free when the finalizer is called (after it is marked for collection by the GC and placed in the finalizer queue), but if there isn't any finalizer and Dispose() is never called, then memory can be leaked and only re-claimed when the process terminates.

Mark Cidade
  • 94,042
  • 31
  • 216
  • 230
1

Failing to call IDisposable on objects which subscribe to events from longer-lived objects will extend the memory-allocation lifetime of the subscriber to be extended to that of the publisher. If there is no upper bound to the number of subscribers that may be attached and abandoned during a publisher's lifetime, this will constitute an unbounded memory leak.

supercat
  • 69,493
  • 7
  • 143
  • 184
1

The big issue is when the GC runs.

Take the following class

class GcTest
{
    private Stopwatch sw = new Stopwatch();
    public GcTest()
    {
        sw.Start();
    }

    ~GcTest()
    {
        sw.Stop();
        Console.WriteLine("GcTest finalized in " + sw.ElapsedMilliseconds + " ms");
    }
}

Put a breakpoint on the Console.WriteLine if you like.

Create an empty windows forms app, and in the form load event just instantiate a new GcTest

private void Form1_Load(object sender, EventArgs e)
{
    var gcTest = new GcTest();
}

Run your application and wait for the finalizer to run.
Most likely it won't run until you close your application.

Albin Sunnanbo
  • 44,354
  • 8
  • 64
  • 104
0

To my knowledge the GC will not call Dispose. You have to call it yourself explicitly or use using. So the answer is: Yes, if the class handles unmanaged resources, which are released in Dispose, your class will leak, if you don't call Dispose.

Achim
  • 14,333
  • 13
  • 70
  • 128
  • 3
    This isn't strictly true as it will only be "leaked" until the finalizer runs. That's based on the big assumption they have properly implemented `IDisposable`. – user7116 Jul 28 '11 at 20:42
  • 2
    But any proper IDisposable class will also release the resources in the destructor (GC). – Henk Holterman Jul 28 '11 at 20:44
  • 1
    @Henk Holterman: IDisposable classes which can, practically, recover from improper abandonment using Finalize should do so. Such recovery is not always practical, however. Code which abandons IDisposable objects without knowing that *those particular objects* can be safely abandoned is far more broken than IDisposable objects which leave things in a bad state when abandoned. – supercat Jul 29 '11 at 00:08