Debugging ASP .NET memory leak - watch for static references

A while ago, I needed to debug an ASP .NET website, which was eating more and more memory the longer it was running, and never seemed to release the memory again. Even though the number of concurrent users was more or less constant, the memory usage seemed to be proportional to the total number of sessions since last application restart.

What was happening here ? Could it be session-state elements that never got garbage collected ? To find out, I simulated load on a test version of the website, let the sessions time out and used WinDbg and the Son of Strike dll extension command "gcroot" to find out which objects was stll alive and why they could not be collected by the garbage collector. First I forced a few garbage collections by calling GC.Collect() to make sure that the garbage collector had actually run.

I found out that some objects, that we keep in Session was rooted to an object array, which, it turned out, represented static references.
After some investigation, I found the problem in the code: A developer had created a static event, and had the objects in session subscribe to this event. This meant that the session objects could not be collected, since there were still reachable references to the objects.

It turned out that the event did not need to be static, so the fix was rather easy to implement. The debugging of the issue, however, took some time.

The morale of this is, that even though we have managed code with memory management, you should still keep an eye on memory usage during development. And with ASP .NET, you should be very careful with static references, since they could root your objects so that they can't be garbage collected.

You should also be aware, that static objects are shared for all users. This should not come as a surprise, if you know just a bit about the ASP .NET process model, but it is my experience that it is easy to forget, even for experienced developers. Because the static objects are shared, you should treat them as such, and remember to use proper synchronization methods when accessing them..

I might blog more in the future about this kind of issues.