LocalVars: A Model For Eliminating Memory Leaks

Any experienced programmer in the world has at least once encountered memory leaks in his programs. Memory leaks are usually discovered in programs that run for a long time, in most cases these programs are daemons. The thing runs for a week, you do a 'ps', and there it is, a giant program consuming up nearly all memory resources -- but wasn't this program meant to be small and not actually do very much at all? Probably so.

Memory leaks can easily occur when the programmer forgets to free a piece of allocated memory. Usually this happens when functions are used that implicity call malloc(), like for instance strdup() does. Leaks can also occur if an object is removed from a linked list, and the programmer cleanly sets the pointer to NULL but has forgotten to actually delete the dynamic object.

These are actually quite harmless bugs. The memory leakage is small, and the operating system will clean it up nicely when the program exits. The leak is likely to go unseen, until the program runs for a long time, and then it may become a problem, especially if the leaking routine is called from within a loop. This is often the case for daemon programs.

Memory leaks can be real hard to track down. Like with all bugs, preventing leaks is better than having to try to find and fix them. There are (mostly commercial) tools out there that help you find leaks, but this article is not an advertisement for those tools. Instead, I will show you a programming model that can (if used properly) eliminate any memory leaks in your code.

One day I was thinking about how interpreters work, they never seem to have any problems with scripts leaking memory. People often say that it is handled by garbage collection and keeping reference counts, but exactly how would this garbage collection work, and keeping reference counts is just as hard as keeping the number of malloc()s and free()s in balance. Forget to decrement a reference counter once and you'll regret for it the rest of the program's life.
I figured the interpreters do their own management of variable scoping and 'stack' memory. We should be able to pull that stunt too in our programs. I will first give you the declaration of the main structure needed and then I'll explain what we're going to do with it. In pseudo-C:

    struct StackFrame {
        struct StackFrame *stack;               /* stack of StackFrames */
        struct StackFrame *prev, *next;         /* doubly linked list */

        int size, idx;
        char block[1];
    };

A stack frame is a block of memory that is usually on the system stack, but now we are going to manage our own and pretend that it's the system stack. This calls for adding some extra code, but if it gets rid of memory leaks then you have to give some, OK?

At the start of every function we'll call a macro or a function called Enter. Enter will allocate a new stack frame and put it on the global stack of stack frames. Enter will allocate sizeof(struct StackFrame) plus a default size. Now, frame->block nicely points to a block of memory that we can use to declare local variables in. As you already may have noticed, these local variables will be referenced by pointers only. It is not convenient to place your local integer variables in this block. (But then again, have local integer variables ever given you memory leak headaches? No, I thought so.)

If we need a local buffer of 256 bytes long, we take the 256 bytes from the stack frame block buffer. If we need another 32 bytes, we take 32 bytes more. Note that if the buffer is large enough, there's no need to call malloc() every time. We use the idx to denote what space of the buffer already has been allocated. Note that we don't care about memory that is not in use anymore, there is no deallocate option for these local variables. If you are confused at this moment, read on and it will clear up later. Suppose you need a buffer of a couple of kilobytes large, and run out of local variable space, you can allocate a new stack frame and put it on the list (rather than on the stack). I guess you could also use realloc() to grow the current stack frame, but personally, I don't like the use of realloc().
At the end of the routine, we call a routine called Leave. What Leave does is simply pop the top stack frame off the stack and free it. Freeing up this stack frame automatically frees up all locally allocated variables.

A minor issue arises when you wish to return a locally allocated variable to the parent routine. Instead of calling Leave, you should then call Return. Return will take the stack frame containing the variable you wish to return off the list containing the current stack frame(s). Then, Return will destroy the current stack frame just as Leave did. Finally, Return will add the stack frame containing our variable to the list of the current stack frame. By doing so, it moves the variable from the scope of this subroutine to the scope of the parent routine.

Our code now looks something like this:

    char *foo(int bar) {
    char *p;

        Enter();

        p = LocalVar(256);
        sprintf(p, "bar equals %d", bar);

        Return(p);
    }


    void fubar(int pick_a_number) {
    char *str;

        Enter();

        str = foo(pick_a_number);
        printf("fubar: %s\n", str);
        str = NULL;                   /* does nothing, really */

        Leave();
    }

This does not produce a memory leak, even though we never explicitly deallocate the allocated string.
Last but not least, when using a main loop, be sure to put the loop around the subroutine call rather than inside the subroutine. This way you ensure that Leave is called, and Leave is the one that keeps our street clean.

Purists may say we didn't solve the strdup() problem, as it still can produce memory leaks. All I can say to that is to stay away from routines that implicitly allocate memory as much as possible, and use a LocalVar and strcpy() instead. If you stick to the model, no harm will come to you or your programs, and there will be no longer the need to restart that daemon every night.


If you really must, you can contact the author at walter at heiho dot net