tags:

views:

531

answers:

8

Hi.

Please can anyone recommend a quick checklist / best practice guide to help us avoid simple (but subtle) mistakes that cause could cause memory leaks in .net apps

I find it difficult and quite painful to begin searching for the cause of a memory leakage when i'm in the testing phase of a project.

If there are 'rules of thumb' to completely guide against memory leaks in managed applications, i implore you to please share your experience.

Thanks.

(I thought Managed applications are suppose to be 'memory managed' i.e. the GC? Why then do we still find leakages in purely managed code?)

+3  A: 

Number one would be event handlers that are attached and never detached.

If the subscriber to the event lives longer than the producer of the event and the subscriber doesn't disconnect the event handler then object firing the events will remain alive as there's still a reference to it through the subscribing object.

Craig Shearer
+2  A: 

The number one cause of memory leaks in managed applications, in my opinion, is mistakenly believing that you have a memory leak, when in fact you haven't measured properly. Be sure to use a memory profiler to measure exactly what kind of memory leak you have. You may find that you have some other problem.

But when the leak is real, the major cause would be references to objects keeping those objects live when they're not really needed. Implementing a using statement for almost all objects implementing IDisposable is a good start. The only exception I know of is WCF proxy classes, which should be accessed with a try/catch/finally, and not a using statement.

John Saunders
Any reference to articles why try/catch/finally should be used over using with WCF proxies?
Peter Lillevold
See http://www.iserviceoriented.com/blog/post/Indisposable+-+WCF+Gotcha+1.aspx
John Saunders
+3  A: 

To answer your last two questions:

There are, by definition, no memory leaks in managed code. There are two kinds of leaks that can occur:

  • Objects are released when no references to the object are alive. If you still have a reference to an object, it will not be released. This happens for instance when you throw away a reference to an object that has registered for an event - if you don't manually unregister the event handler (or use a weak reference), the event will still reference the object, so it will not be released although you have no obvious reference to it anymore.
  • Unmanaged resources can be leaked. Typically, a wrapper for unmanaged resources implements IDisposable and will free the resource when you call Dispose. If you just throw away the object, it will not release the resource and therefore leak it.

So, two rules of thumb are:

  • Unregister any event handler when you release an object, or use a weak reference for that (search on SO, it's explained somewhere).
  • If a class exposes a Dispose method, call it. If you use the object only temporarily, use the using construct. If you have members that implement IDisposable, implement IDisposable yourself and call the members' Dispose in your Dispose.
OregonGhost
+5  A: 

There are many forms of leaks:

  • Unmanaged leaks (code that allocates unmanaged code)
  • Resource leaks (code that allocates and uses unmanaged resources, like files, sockets)
  • Extended lifetime of objects
  • Incorrect understanding of how GC and .NET memory management works
  • Bugs in the .NET runtime

The first two is usually handled by two different pieces of code:

  • Implementing IDisposable on the object and disposing of the unmanaged memory/resource in the Dispose method
  • Implementing a finalizer, to make sure unmanaged resources are deallocated when GC has found the object to be eligible for collection

The third, however, is different.

Say you are using a big list holding thousands of objects, totalling a significant size of memory. If you keep around a reference to this list for longer than you need to, you will have what looks like a memory leak. In addition, if you keep adding to this list, so that it grows with more data periodically, and old data is never reused, you definitely have a memory leak.

One source of this I've seen frequently is to attach methods to event handlers but forget to unregister them when you're done, slowly bloating the event handler both in size and code to execute.

The fourth, an incorrect understanding of how .NET memory management works can mean that you look at the memory usage in a process viewer and notice that your app keeps growing in memory usage. If you have lots and lots of memory available, GC might not run that often, giving you an incorrect picture of the current usage of memory, as opposed to mapped memory.

The fifth, that's harder, I've only seen one resource management bug in .NET so far and afaik it has been slated for fix in .NET 4.0, it was with copying the desktop screen into a .NET image.


Edit: In response to the question in the comments, how to avoid keeping references longer than necessary, then the only way to do that is to to just that.

Let me explain.

First, if you have a long-running method (for instance, it might be processing files on disk, or downloading something, or similar), and you used a reference to a big data-structure early in the method, before the long-running part, and then you don't use that data structure for the rest of the method, then .NET, in release-builds (and not running under a debugger) is smart enough to know that this reference, though it is held in a variable that is technically in scope, is eligible for garbage collection. The garbage collector is really aggressive in this respect. In debug-builds and running under a debugger, it will keep the reference for the life of the method, in case you want to inspect it when stopped at a breakpoint.

However, if the reference is stored in a field reference in the class where the method is declared, it's not so smart, since it's impossible to determine whether it will be reused later on, or at least very very hard. If this data structure becomes unnecessary, you should clear the reference you're holding to it, so that GC will pick it up later.

Lasse V. Karlsen
+1 for detail and nicely written
J M
yeah, very well written and good content, +1 from me too
CharlesO
>>Say you are using a big list holding thousands of objectswhat is a way around this. alot of us do this alot. all i can think of is assigning and reusuing existing objects from a stack that way no unusewd objects are left around in a large list
CharlesO
+2  A: 

The short answer is non-obvious references.

To add some details:

  • Statics are not collected until AppDomain is collected (which may equal process shutdown)
  • Events (remember to unsubscribe)
  • Blocked finalizers (finalizers are run sequentially so any blocking finalizer will prevent all other finalizable objects from being collected). Example includes finalizers that can't get to a STA thread.
  • Deadlocked thread will never release roots
  • Forgetting to call Monitor.Exit() (e.g. when used with a timeout or across methods) may cause a deadlock which in turn may cause a leak
Brian Rasmussen
A: 

Circular references in value-classes (commonly in the model-layer of your application) can easily hide their leaks as well. In theory, don't do them, in practice, be aware whenever you need to do them :)

cwap
+1  A: 

I can start to suggest that one should always be aware of IDisposable objects and how to properly dispose them. Also, be very careful with the state in you app. Grab only objects when it's absoluttly necessary. If object A is goind to live for a long period of time in your app, always try not making dependencies between A and other objects.

bruno conde
+3  A: 

One of the many good things you can do to manage memory effectively.

Call dispose on any object that implements IDisposable wherever possible especially on the DataSet or DataTable object.

Better still use the using{} constructs on these objects.

Binoj Antony
+1 for the USING{} - this has helped me solve definite memory leaks in the past!
Doug L.