Three techniques for tracking down memory leaks due to undisposed objects - Rico Mariani's Performance Tidbits - Site Home - MSDN Blogs
> Three techniques for tracking down memory leaks due to undisposed objects
15 Apr 2005 2:37 PM
People often ask me for tips/tricks on how to find out which objects are not being properly disposed. But before I go any further I should talk briefly about why that's a problem.
If an object is Finalizable that means that the runtime must run the Finalize method on that object (in C# you use the ~Class syntax to define the finalizer) before the memory can be reclaimed. The mechanism for doing this is fairly straightforward -- whenever an object that is finalizable first becomes unreachable it is put on a queue of objects needing finalization. Naturally we only discover that the object is unreachable during a garbage collection, and since we need to keep the object alive to finalize it the object must survive that collection. When the finalizer runs the object will be again unreachable and on the next collection the memory will be recovered as usual. See Garbage Collector Basics and Performance Hints and Two Things to Avoid for Better Memory Usage for more details.
To avoid these problems, every finalizable object should also be IDisposable (and most are). If an object is IDisposable it means that when you are done with it you can call the Dispose() method which releases any unmanaged resources associated with the object and tells the runtime that the object no longer needs to be finalized. As a consequence the resources are returned to the system as quickly as possible and the object doesn't need to survive for finalization -- it dies nice and quick like a regular object.
So, how do we detect that this is happening? And if it is happening how do we track down the source of the leak? I have some approaches for now and some hints as to how we'll do this in the future.
Technique #1 If you want to know if finalization is a problem any regular profiler can give you a good hint. Using CLR Profiler for instance you can run your application then use the Call Tree view and select the various tabs until you find the one for the finalizer thread. It's very obvious which one that is because all the method names will be things like "SomeClass::Finalize". A lot of times just knowing which classes are being finalized a lot is enough to fix the problem.
If you see a lot of finalization but the finalizer is associated with a class or classes for which there are many instances and you want to know which instance is the bad one there is more you can do. If the class is one you control you can call store a stacktrace in the object when it is created with "StackTrace(Thread targetThread, bool needFileInfo)" (note this is a heavyweight thing to do, not something you want to ship with) then when the object is finalized you can dump that stack trace so that you will see what the stacktrace created the object and you can target that trace for elimination. To keep the cost down you might take a stacktrace at random only 1/1000th of the time and you could still get enough samples of the bad pattern to identify it over a long run.
If you don't own the class then its harder but you still might be able to make some headway. The first thing you need is a durable way to identify a particular instance of a particular class -- you can't use an object pointer for this because the object can move over time but for many classes (almost all reference types) you can hash the class using GetHashCode() and the hash doesn't change over the life of the object. Hash the objects you care about as soon as you can and capture the best callstack you can to finger their creator (see above). Next use your favorite debugger to stop in the Finalizer for the classes in question -- once you are stopped there dump the object's hash code in the debugger -- you should be able to put a call to GetHashCode() in the debugger watch window for instance. You can use that hash code to find the callstack that created the object. If the callstack is uninteresting or too expensive you can use any other friendly description of the callsite that will help you diagnose the originating location.
The profiling API (the one that CLR Profiler uses for instance) in V1.0 and V1.1 provides almost enough information to do this job more automatically. In V2.0 (Whidbey) we're doing more:
Three techniques for tracking down memory leaks due to undisposed objects - Rico Mariani's Performance Tidbits - Site Home - MSDN Blogs
> Three techniques for tracking down memory leaks due to undisposed objects
15 Apr 2005 2:37 PM
People often ask me for tips/tricks on how to find out which objects are not being properly disposed. But before I go any further I should talk briefly about why that's a problem.
If an object is Finalizable that means that the runtime must run the Finalize method on that object (in C# you use the ~Class syntax to define the finalizer) before the memory can be reclaimed. The mechanism for doing this is fairly straightforward -- whenever an object that is finalizable first becomes unreachable it is put on a queue of objects needing finalization. Naturally we only discover that the object is unreachable during a garbage collection, and since we need to keep the object alive to finalize it the object must survive that collection. When the finalizer runs the object will be again unreachable and on the next collection the memory will be recovered as usual. See Garbage Collector Basics and Performance Hints and Two Things to Avoid for Better Memory Usage for more details.
To avoid these problems, every finalizable object should also be IDisposable (and most are). If an object is IDisposable it means that when you are done with it you can call the Dispose() method which releases any unmanaged resources associated with the object and tells the runtime that the object no longer needs to be finalized. As a consequence the resources are returned to the system as quickly as possible and the object doesn't need to survive for finalization -- it dies nice and quick like a regular object.
So, how do we detect that this is happening? And if it is happening how do we track down the source of the leak? I have some approaches for now and some hints as to how we'll do this in the future.
Technique #1 If you want to know if finalization is a problem any regular profiler can give you a good hint. Using CLR Profiler for instance you can run your application then use the Call Tree view and select the various tabs until you find the one for the finalizer thread. It's v
ery obvious which one that is because all the method names will be things like "SomeClass::Finalize". A lot of times just knowing which classes are being finalized a lot is enough to fix the problem.
If you see a lot of finalization but the finalizer is associated with a class or classes for which there are many instances and you want to know which instance is the bad one there is more you can do. If the class is one you control you can call store a stacktrace in the object when it is created with "StackTrace(Thread targetThread, bool needFileInfo)" (note this is a heavyweight thing to do, not something you want to ship with) then when the object is finalized you can dump that stack trace so that you will see what the stacktrace created the object and you can target that trace for elimination. To keep the cost down you might take a stacktrace at random only 1/1000th of the time and you could still get enough samples of the bad pattern to identify it over a long run.
If you don't own the class then its harder but you still might be able to make some headway. The first thing you need is a durable way to identify a particular instance of a particular class -- you can't use an object pointer for this because the object can move over time but for many classes (almost all reference types) you can hash the class using GetHashCode() and the hash doesn't change over the life of the object. Hash the objects you care about as soon as you can and capture the best callstack you can to finger their creator (see above). Next use your favorite debugger to stop in the Finalizer for the classes in question -- once you are stopped there dump the object's hash code in the debugger -- you should be able to put a call to GetHashCode() in the debugger watch window for instance. You can use that hash code to find the callstack that created the object. If the callstack is uninteresting or too expensive you can use any other friendly description of the callsite that will help you diagnose the originating location.
The profiling API (the one that CLR Profiler uses for instance) in V1.0 and V1.1 provides almost enough information to do this job more automatically. In V2.0 (Whidbey) we're doing more: