On finding (and fixing) memory leaks
Recently I needed to find a fix a memory leak an application I have been working on. Now the application is quite big and I am by no means the only developer so I didn't exactly have an obvious starting point. Fortunately I do have a copy of the ANTS Profiler, see
http://www.red-gate.com/, and it turned out to be more that just useful, it’s a real life saver, in these kind of circumstances.
Turns out there where multiple memory leaks.
Lesson 1: never think you are done after fixing a problem, always make sure there isn't a second one.
Using the ANTS Profiler to create a number of snapshots of the running application I noticed that there where numerous copies of an object that I thought was supposed to pretty much a singleton. I say pretty much because a second copy can be around during specific circumstances but they should not be around very long. Turned out that they where kept in memory by a delegate.
Lesson 2: Delegate have a reference to the target object and that will keep it in memory.
So there where too many in memory and I knew what was keeping them there but what caused them to be there in the first place? It turned out to be because of cloning. One of the things we do in this application is allow the user to undo and redo pretty much everything. So we track state by creating clones of the original objects. Works great as long as these objects aren't too large, something that was supposed to be the case. We actually use a generic CloneObject function that will clone every serializable object. This works as follows:
publicstatic T CloneObject<T>(object obj)
{
T newObject;
MemoryStream ms = new MemoryStream();
BinaryFormatter serializer = new BinaryFormatter();
serializer.Serialize(ms, obj);
ms.Position = 0;
newObject = (T)serializer.Deserialize(ms);
ms.Close();
return newObject;
}
Yes it’s a C# project :-)
Now this does a deep clone, meaning it will not just serialize the object itself but also everything it points to. Very useful if you want to clone a set of related objects, like a header with collection of details, but if someone adds a reference to another object that will get cloned to! And that was exactly what was happening in our case.
Lesson 3: Even simple code can cause difficult problems.
So tracking this down using the ANTS profiler was not to hard, in fact I can't even imagine doing without it. But even a useful tool like the ANTS profiler could do with some improvements. The two things I missed most where:
- Seeing how an object is rooted.
- Seeing which objects where ready for garbage collection but not collected yet.
The first is needed because the .NET garbage collector removes all objects that cannot be reached from a root object. So if it is still there I need to know how an object is tooted. Now I needed to track this down using the object hierarchy but because it leads to numerous paths it is time consuming to find all the paths.
The second is needed because some objects might be ready for garbage collection making it pointless to track them. In fact the ANTS Profiler does a GC.Collect() before taking a snapshot but any object that implements IDisposable will be disposed and only collected during the next garbage collection cycle. Due to this behavior they will still be in the snapshot.
Now as it turns out Red Gate is planning a new version and they are already planning to address both issues. In fact the have an ongoing survey where people can vote on or suggest features. I suggest you go to
http://www.surveymonkey.com/s.asp?u=537852784382 and participate, it will only take a few minutes and provide them with valuable feedback.