What are some tips I can use to avoid memory leaks in my applications? Are there any gotchas or pitfalls that I can look out for?
Call Dispose on IDisposable objects or use the using
clause. That should take care of most of the leaks I can think of.
It's managed code, that c#, so you have to try hard to leak memory :P
As mentioned by ocdecio be sure to call Dispose on Idisposable objects, and remember to remove event handlers when you're done with an object. When building classes that works with unmanaged resources, be sure to implement Idisposable, so the user will know that there are critical resources that'll need to be disposed of.
Also, even though garbage collections do quite a bit a work for you, you should get rid of references to objects that you're done with. Else they'll still have a root, and they won't be GC'ed.
Watch that you remove any event handlers that you use. In .NET, they are the most common cause of leaked memory.
Having a basic understanding of how the garbage collector works will help you avoid abusing memory. For example, if you are keeping a reference to an object you no longer need, the gc won't be able to collect it.
Along the same lines, if you're storing data the the user enters, or data that is added over time, you should consider some kind of limitations so that your memory usage doesn't grow indefinitely.
I know some people are going to advise garbage collection as the solution. But there are lots of cases where garbage collection doesn't give you the results that you expect. It is easy to end up holding on to a stray reference which prevents whole chains of memory from being freed. Read about how this torpedoed a DARPA Grand Challenge entry. You can argue these aren't memory leaks but if the program expects that memory to be freed it is still a problem. Just like in C programming, after a couple of months you get the hang of how to make sure you don't leave unwanted references behind.
I've ran into issues where an object (Ping) implemented Dispose() twice, by implementing the IDisposable interface and inheriting it at the same time. The inherited method did nothing, and as a result you had to cast the object to IDisposable when calling Dispose() or it would leak memory. Here's a post I wrote a few years ago with more detail.
Most memory leaks that I have encountered in .NET has been related to using COM objects and not releasing them properly. As soon as I see a reference to a COM object, I think "memory leak".
Use "using" keyword to automatically call Dispose() method of IDisposable object.
For any COM interop you have to manually release all resources.
- Wrap anything which is disposable in a using construct.
- Avoid COM objects.
- Check to see that all event hanlders are being removed properly.
- Check to see that all data bindings are being removed properly.
- Keep it simple
If your application logic is getting needlessly complex, you might start ending up with memory leaks. If you keep your classes small and follow general coding practices you probably won't run into any memory leaks with managed code. It is possible, but not as likely as it use to be.
If you suspect a memory leak, use a profiler to see if any objects are being kept around longer than needed.
The last time I ran into a serious memory leak was .NET 1.1, it turned out there was a bug in the framework.
Memory leaks are bugs, so in general - the question could be answered the same as "how to code without bugs"? In the long run - it is not possible to have no bugs, but you can limit the chance for having those in the released code.
Start with caring about developed code quality and following the guidelines mentioned by others.
- Simplicity is golden - the more simple the code - the less a chance for bugs or leaks
- Be careful when using unmanaged resources - Windows Handles, DB connections, GDI objects, etc.
- Use using for IDisposables
- Implement IDisposable for classes that carry unmanaged resources
- Make sure to remove any references to unused objects - including the tricky event handlers
On top of these - implement tests to see if the memory leaks - unit, concurrency, stress and load tests could help here most. See if memory leaks by checking metrics (perf counters). You could also log object creations and destructions and analyze the logs at the end of a test.
The most common case of memory not being destroyed by the GC is when you have event handlers that did not get unhooked properly. You can unhook in Dispose() if you want.
I describe the problem more in detail, and I have a way to write tests to determine if you leak the object here.
As others have said call Dispose() (or use a using statement), but additionally consider whether classes use resources and if so implement IDisposeable. This is most often the problem in my code is that I have a class with member that doesn't get cleaned up till a GC.
Don't underestimate the helpfulness of tools in these situations. The .NET memory profilers are fairly mature and robust nowadays, and if you have a complicated application where an object that you think should be freed is still held as a reference by something else the ability to pinpoint that reference is invaluable.
I've just finished hunting down a memory leak where a WPF tab page hosted a Windows Form control (since these tabs held a lot of data and you could open and close them at will simply waiting until the GC cleared the memory on close was not an option). I used the YourKit profiler to take a snapshot of the memory before the tab was opened, opened a tab, closed it again and took another snapshot. Inside the profiler you could visually compare the two snapshots and see what objects had survived the process and follow their references back to the GC root. I have no experience with other profilers, but I know there are a few out there if YourKit doesn't fulfil your needs.
Edited to add:
Okay, this isn't avoiding memory leaks, it's fixing them. But I'll leave it here since I think it is useful information and I don't think that enough .NET developers know about these tools.
Types which implement a finalizer may leak, if any one finalizer blocks for some reason. I have seen finalizers block due to locking and thread apartment issues.
As instances of types with finalizers are not collected until their respective finalizers have run a single blocking finalizer will block any other finalizable objects pending collection.
First, let me bring share my strict understanding of a memory leak. My definition of a memory leak is when you have memory you have allocated and no longer a reference to it, so it is not possible to free that memory. Having said that, it is impossible to have a memory leak in .net objects (instances of CTS types that live in the managed heap, I mean). Unreferenced memory is precisely what the GC looks for to free.
Having said that, one can have a more lax understanding of what a memory leak is. If you consider a memory leak to be a uncontrolled growth of the memory being used, well, that is very easy. Just make a big misuse of static variables, mostly ones that reference huge lists. If you keep those objects referenced, the GC will never clean them, promoting them to higher generations and making them even harder to collect. Even though this is not a memory leak in the strict sense, in the end of the day it can lead to similar symptoms. A good way to try to detect this kind of "leak" is to use the CLR Profiler.
Another source of "leaks" is through improper use of event handlers as previously stated. Every time that object A registers one of its instance methods with an event at object B, object B ends keeping an indirect reference to object A, which means that while B is alive, A will be kept alive. Please note however, that there is no circularity here. As soon as neither B or A have any root reference, no matter how many cross references they have, they will eventually be collected.
Finally, one can actually induce a memory leak in .net, but never (at least theoretically) when talking about managed memory, as the GC does an excellent job in clearing that. If any of your objects maintains a reference to unmanaged memory, for instance through interop, then that memory needs to be explicitly cleaned. Failing to do so can lead to a memory leak, indeed. Even though I've never experienced such a scenario, at least in theory this can happen. As previously stated, objects that hold such references ought to implement IDiposable in order to clear that memory and their usage should guarantee Dispose is invoked for that purpose, mainly through the usage of the using clause. Notice that Dispose will not free the object's memory, but will only ask the object to release any unmanaged memory it is referring to.
One special kind of unmanaged memory is the one needed by COM objects used in interop scenarios. These objects are accessed through Runtime Callable Wrappers, RCWs for friends, but have no Dispose. "Using" will not work. The way to release the underlying COM objects is through the static method:
System.Runtime.InteropServices.Marshal.ReleaseComObject(object);
Since "using" is only syntax sugar to call IDisposable.Dispose() in a finally block, it is not usable with RCWs, hence don't forget to place the call in ReleaseComObject(object) in a finally block yourself so you ensure it is really called.
You may find my new article useful: How to detect and avoid memory and resources leaks in .NET applications