Wednesday, 19 October, 2005
Hunting Memory Leaks
One of the things I like best about the .NET Framework is the automatic memory manager. Not having to worry about freeing memory is a huge load off a programmer's mind. Old time C and C++ programmers scoff at the idea of garbage collection, for two reasons. The first is the curmudgeon: "I don't need no fancy garbage collector. By golly, if a programmer can't remember to free the memory he's allocated then he shouldn't be programming."
It'd be nice to live in such a perfect world. The fact is that all too often programmers don't free the memory that they've allocated. Even really good programmers forget, especially when they're faced with an API that doesn't make it clear who is responsible for freeing memory. All too many programmers write APIs that have the screwiest side-effects. It's often difficult to tell which functions allocate memory and, if they do, when or if it's safe to free the memory. In addition, maintenance programmers often end up allocating memory in an object's constructor and conveniently forget to free the memory in the destructor. There are tools that will help you identify memory leaks, but many programmers either don't know the tools are available or don't understand that memory leaks are almost inevitable. Especially when you go into maintenance mode.
The other reason that C++ programmers don't like the .NET garbage collector is something called "nondeterministic finalization." In short, there's no way to tell exactly when an object's destructor will be called. This can be a problem in some relatively rare circumstances, but there are ways around it. Mostly, programmers fixate on the nondeterministic finalization argument because it's the only solid argument against garbage collection. Nevermind that it's a non-issue in all but a handful of cases that most programmers will never encounter.
I'm in the process of removing memory leaks from a couple hundred thousand lines of old C and C++ code that I inherited. There are some very common patterns to the way that memory is leaked in this code. Most of the bugs fall into two categories: memory that's allocated when an object is created and not released when the object is destroyed, and memory that's allocated as a side-effect of calling a function and not released by the caller. The first category is usually caused by maintenance programmers. The second is the result of non-obvious side effects.
This code, by the way, is not just one isolated system that suffers from memory leaks. Every C, C++, and Delphi project that I've been called in to work on has had memory leak problems. Proper memory management is very difficult. The .NET Framework's use of garbage collection to avoid memory leaks is a Good Thing.