56

Is it ever possible in a managed system to leak memory when you make sure that all handles, things that implement IDispose are disposed?

Would there be cases where some variables are left out?

jpbochi
  • 4,366
  • 3
  • 34
  • 43
Joan Venge
  • 315,713
  • 212
  • 479
  • 689
  • Also, I've seen IDisposable not implemented properly on classes people have written. It does happen. Real edge case of course, and you're probably just talking about using stuff already in the framework. – core Mar 21 '09 at 20:46
  • Possibly, as I understand, an object is disposed once there is no more reference to the object. What if two objects referencing to each other and you forgot to remove the reference from one of them. – sss Aug 14 '14 at 06:08
  • @Noypi Gilas: That's a common mistake to assume. As far as I know the garbage collectors never _dispose_ anything, they don't even know about the IDisposable interface. Garbage collectors only know about finalizers which they invoke when an object gets unreachable (and the according finalizer is not suppressed). – Christoph Mar 16 '21 at 10:11

21 Answers21

65

Event Handlers are a very common source of non-obvious memory leaks. If you subscribe to an event on object1 from object2, then do object2.Dispose() and pretend it doesn't exist (and drop out all references from your code), there is an implicit reference in object1's event that will prevent object2 from being garbage collected.

MyType object2 = new MyType();

// ...
object1.SomeEvent += object2.myEventHandler;
// ...

// Should call this
// object1.SomeEvent -= object2.myEventHandler;

object2.Dispose();

This is a common case of a leak - forgetting to easily unsubscribe from events. Of course, if object1 gets collected, object2 will get collected as well, but not until then.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
41

I don't think C++ style memory leaks are possible. The garbage collector should account for those. It is possible to create a static object that aggregates object references even though the objects are never used again. Something like this:

public static class SomethingFactory
{
    private static List<Something> listOfSomethings = new List<Something>();

    public static Something CreateSomething()
    {
        var something = new Something();
        listOfSomethings.Add(something);
        return something;
    }
}

That's an obviously stupid example, but it would be the equivalent of a managed runtime memory leak.

strager
  • 88,763
  • 26
  • 134
  • 176
Michael Meadows
  • 27,796
  • 4
  • 47
  • 63
  • I've also heard this sort of thing called a "core cancer" -- it grows without bound. – Jeffrey Hantin Mar 06 '09 at 22:49
  • 7
    This example isn't as stupid as you think. The SharePoint object model has exactly this problem. I suspect they are caching objects that have to be retrieved from a backing store, but if you access many such objects, you will find yourself out of memory in relatively short order. – Ben Collins Mar 06 '09 at 23:01
  • Yeah, not a stupid example. I have code that is pretty much just that, where FileSystemWatcher objects are created once, then stored in a static collection until the program shuts down. All they do is monitor some files for change. – core Mar 21 '09 at 20:42
  • In this example what is is being leaked? is it the List, or the var something or both? I am assuming the list but I am not sure. – corymathews Sep 04 '09 at 14:40
  • 1
    The references are being "leaked" into a list so that those objects are never disposed. – RCIX Oct 05 '09 at 23:39
  • I wrote a console program to illustrate this leak here: https://gist.github.com/tbowers/cea8721a24e857bd8e78. Every time you hit the key, another `Something` is created and you see ~40mb allocated. What I don't get is even if I remove the `List<>` part of this, I still don't see the memory reclaimed after the each `while` iteration. Is it just that I'm waiting on the GC to collect? – Ternary Oct 02 '14 at 16:17
  • @Ternary this is probably because your objects survived a generation 0 collection cycle. Read http://msdn.microsoft.com/en-us/library/ee787088(v=vs.110).aspx paying special attention to the sections on "Conditions for a garbage collection" and "Generations" – Michael Meadows Dec 11 '14 at 18:23
28

As others have pointed out, as long as there's not an actual bug in the memory manager, classes that don't use unmanaged resources won't leak memory.

What you see in .NET is not memory leaks, but objects that never get disposed. An object won't get disposed as long as the garbage collector can find it on the object graph. So if any living object anywhere has a reference to the object, it won't get disposed.

Event registration is a good way to make this happen. If an object registers for an event, whatever it registered with has a reference to it, and even if you eliminate every other reference to the object, until it unregisters (or the object it registered with becomes unreachable) it will stay alive.

So you have to watch out for objects that register for static events without your knowledge. A nifty feature of the ToolStrip, for instance, is that if you change your display theme, it will automatically redisplay in the new theme. It accomplishes this nifty feature by registering for the static SystemEvents.UserPreferenceChanged event. When you change your Windows theme, the event gets raised, and all of the ToolStrip objects that are listening to the event get notified that there's a new theme.

Okay, so suppose you decide to throw away a ToolStrip on your form:

private void DiscardMyToolstrip()
{
    Controls.Remove("MyToolStrip");
}

You now have a ToolStrip that will never die. Even though it isn't on your form anymore, every time the user changes themes Windows will dutifully tell the otherwise-nonexistent ToolStrip about it. Every time the garbage collector runs, it thinks "I can't throw that object away, the UserPreferenceChanged event is using it."

That's not a memory leak. But it might as well be.

Things like this make a memory profiler invaluable. Run a memory profiler, and you'll say "that's odd, there seem to be ten thousand ToolStrip objects on the heap, even though there's only one on my form. How did this happen?"

Oh, and in case you're wondering why some people think property setters are evil: to get a ToolStrip to unregister from the UserPreferenceChanged event, set its Visible property to false.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
21

Delegates can result in unintuitive memory leaks.

Whenever you create a delegate from an instance method, a reference to that instance is stored "in" that delegate.

Additionally, if you combine multiple delegates into a multicast delegate, you have one big blob of references to numerous objects that are kept from being garbage collected as long as that multicast delegate is being used somewhere.

Christian Klauser
  • 4,416
  • 3
  • 31
  • 42
17

If you are developing a WinForms application, a subtle "leak" is the Control.AllowDrop property (used to enable Drag and Drop). If AllowDrop is set to "true", the CLR will still hold onto your Control though a System.Windows.Forms.DropTarget. To fix this, make sure your Control's AllowDrop property is set to false when you no longer need it and the CLR will take care of the rest.

Zach Johnson
  • 23,678
  • 6
  • 69
  • 86
8

The only reason for memory leak in .NET application is that objects are still being referenced although their life span has ended. Hence, the garbage collector cannot collect them. And they become long lived objects.

I find that it's very easy to cause leak by subscribing to events without unsubscribing it when the object's life ends.

tranmq
  • 15,168
  • 3
  • 31
  • 27
  • That is not entirely true. A blocking finalizer will keep all remaining finalizable objects from being reclaimed by the garbage collector. I.e. they will be rooted by the internal list of objects waiting to be finalized. – Brian Rasmussen Jul 21 '09 at 07:58
  • 1
    Objects with finalizers on the finalizing queue waiting for their finalizers to be called are still being referenced. – tranmq Jul 24 '09 at 06:56
7

As already mentioned the keeping references around will lead to increasing memory usage over time. An easy way to get into this situation is with events. If you had a long living object with some event that your other objects listen to, if the listeners are never removed then the event on the long lived object will keep those other instances alive long after they are no longer needed.

toad
  • 1,351
  • 8
  • 14
6

You may find my new article useful: How to detect and avoid memory and resources leaks in .NET applications

Fabrice
  • 3,094
  • 3
  • 28
  • 31
5

Reflection emit is another potential source of leaks, with e.g. built-in object deserializers and fancy SOAP/XML clients. At least in earlier versions of the framework, generated code in dependent AppDomains was never unloaded...

Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51
4

It's a myth that you cannot leak memory in managed code. Granted, it's much harder than in unmanaged C++, but there are a million ways to do it. Static objects holding references, unnecessary references, caching, etc. If you are doing things the "right" way, many of your objects will not get garbage collected until much later than necessary, which is sort of a memory leak too in my opinion, in a practical and not theoretical way.

Fortunately, there are tools that can assist you. I use Microsoft's CLR Profiler a lot - it is not the most user friendly tool ever written but it is definitely very useful and it is free.

Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176
2

Once all the references to an object are gone, the garbage collector will free that object on it's next pass. I wouldn't say it's impossible to leak memory but it's fairly difficult, in order to leak you'd have to have a reference to an object sitting around without realizing it.

For example if you instantiate objects into a list and then forget to remove them from the list when you're done AND forget to dispose them.

Kevin Laity
  • 2,489
  • 1
  • 27
  • 34
  • But when the list goes out of scope, that should clear all of the items inside the list? Or does the list persist even if there is a reference to one of its elements? – Joan Venge Mar 06 '09 at 22:49
  • In this case I'm assuming that the list is the member of a class and so gets kept around. You're correct, if the list goes out of scope, it should clear out those references. – Kevin Laity Mar 09 '09 at 13:56
1

Not really a memory leak, but it is quite easy to run out of memory when using large objects (greater than 64K if I remember correctly). They are stored on the LOH and that ist NOT defragmented. So using those large objects and freeing them frees the memory on the LOH, but that free memory is not used anymore by the .NET runtime for this process. So you can easily run out of space on the LOH by using just a few big objects on the LOH. This issue is known to Microsoft, but as I remember now solution for this is being planned.

Oliver Friedrich
  • 9,018
  • 9
  • 42
  • 48
1

A self-reminder How to find Memory Leak:

  • Remove any gc.collect calls.
  • Wait until we have sure the memory is leaking.
  • Create a dump file from the task manager.
  • Open the dump files using DebugDiag.
  • Analyse the result first. The result from there should help us, which issue usually takes most of the memory.
  • Fix the code until no memory leak can be found there.
  • Use 3rd party applications such as .net profiler. (We can use trial, but need to resolve the issue ASAP. The first dump should help us mostly about how the leaking)
  • If the issue is in virtual memory, need to watch the unmanaged memory. (Usually, there is another configuration in there that needs to be enabled)
  • Run 3rd party applications based on how it's used.

Common memory leak issue:

  • Events/delegates are never removed. (When disposing of, make sure the event is unregistered) - see ReepChopsey answer
  • List/Dictionary was never cleared.
  • Object reference to another object that is saved in memory will never disposed of. (Clone it for easier management)
Kosmas
  • 353
  • 4
  • 11
1

It's possible to have leaks if unmanaged resources do not get cleaned properly. Classes which implement IDisposable can leak.

However, regular object references do not require explicit memory management the way lower level languages do.

Ben S
  • 68,394
  • 30
  • 171
  • 212
0

At my last job, we were using a 3rd party .NET SQLite library which leaked like a sieve.

We were doing a lot of rapid data inserts in a weird situation where the database connection had to be opened and closed each time. The 3rd party lib did some of the same connection opening that we were supposed to do manually and didn't document it. It also held the references somewhere we never did find. The result was 2x as many connections being opened as were supposed to be and only 1/2 getting closed. And since the references were held, we had a memory leak.

This is obviously not the same as a classic C/C++ memory leak but for all intents and purposes it was one to us.

Dinah
  • 52,922
  • 30
  • 133
  • 149
0

If it's considered memory leak, it could be achieved with this kind of code, too:

public class A
{
    B b;
    public A(B b) { this.b = b; }
    ~A()
    {
        b = new B();
    }
}

public class B
{
    A a;
    public B() { this.a = new A(this); }
    ~B()
    {
        a = new A(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        {
            B[] toBeLost = new B[100000000];
            foreach (var c in toBeLost)
            {
                toBeLost.ToString(); //to make JIT compiler run the instantiation above
            }
        }
        Console.ReadLine();
    }
}
meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
  • 1
    You should add an explanation of how and why this causes a memory leak. Otherwise people just have to take it on faith. – rollsch Jan 04 '17 at 12:07
0

Small functions help in avoiding "memory leaks". Because garbage collector frees local variables at the end of functions. If function is big and takes a lot of memory you yourself have to free local variables that take a lot of memory and are no longer needed. Similary global variables (arrays, lists) are also bad.

I experienced memory leaks in C# when creating images and not disposing them. Which is a little strange. People say you have to call .Dispose() on every object that has it. But documentation for graphical C# functions doesn't always mention this, for example for function GetThumbnailImage(). C# compiler should warn you about this I think.

Tone Škoda
  • 1,463
  • 16
  • 20
0

In a Console or Win app create a Panel object(panel1) and then add 1000 PictureBox having its Image property set then call panel1.Controls.Clear. All PictureBox controls are still in the memory and no way GC can collect them:

var panel1 = new Panel();
var image = Image.FromFile("image/heavy.png");
for(var i = 0; i < 1000;++i){
  panel1.Controls.Add(new PictureBox(){Image = image});
}
panel1.Controls.Clear(); // => Memory Leak!

The correct way of doing it would be

for (int i = panel1.Controls.Count-1; i >= 0; --i)
   panel1.Controls[i].Dispose();

Memory leaks in calling Controls.Clear()

Calling the Clear method does not remove control handles from memory. You must explicitly call the Dispose method to avoid memory leaks

Daniel B
  • 3,109
  • 2
  • 33
  • 42
0

You can have a memory leak when using the .NET XmlSerializer, because it uses unmanaged code underneath which will not be disposed.

See docs and search for 'memory leak' on this page:

https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializer?view=netframework-4.7.2

FrankyHollywood
  • 1,497
  • 19
  • 18
0

While it is possible that something in the framework has a leak, more then likely you have something that isn't being being disposed of properly or something is blocking the GC from disposing of it, IIS would be a prime candidate for this.

Just remember that not everything in .NET is fully managed code, COM interop, file io like file streams, DB requests, images, etc.

A problem we had a while ago (.net 2.0 on IIS 6) was that we would create an image and then dispose of it but IIS wouldn't release the memory for a while.

Bob The Janitor
  • 20,292
  • 10
  • 49
  • 72
0

The only leaks (other than bugs in the runtime which may be present, though not terribly likely due to garbage collection) are going to be for native resources. If you P/Invoke into a native library which opens file handles, or socket connections, or whatever on your managed application's behalf, and you never explicitly close them (and don't handle them in a disposer or destructor/finalizer), you can have memory or resource leaks because the runtime cannot manage all of those automatically for you.

If you stick with purely managed resources, though, you should be just fine. If you experience any form of memory leak without calling into native code, then that's a bug.

Michael Trausch
  • 3,187
  • 1
  • 21
  • 29
  • No one is stopping you from creating tons of objects that your program doesn't *use*. When they are referenced in some static field, they don't go out of scope automatically, they have to be "disconnected" from the graph of objects explicitly. – Christian Klauser Mar 06 '09 at 23:07