Memory management issues

I think the typical progression of one’s concern for memory management issues is like this:

  1. What’s memory management?  (Alternatively: What’s memory?)
  2. How is malloc() (or new) any different from declaring a new variable?
  3. If I can free() (or delete) a null pointer, why do I have to check malloc() (or new)?
  4. Why do I still have memory leaks?

I don’t remember how I myself passed through these stages, but since point 4 is quite irritating I feel that this post is justified.  Let me “recall” the answers: memory management is the practice of increasing or decreasing the amount of space your program has available to work.  If you don’t do it, then the only space you have is whatever variables you declare in the code, which the compiler either handles by embedding an air pocket into the executable file to hold them, or by juggling them in and out of registers in the CPU (or both).  Or even by doing memory management without telling you.

The allocation functions malloc() or (in C++) new acquire this space, returning a pointer to it. This is not the same as declaring a new variable for two reasons: first, you might not know how much space you need until the code runs, in which case you can’t ask for the appropriately-sized variable. And second, the compiler now has no idea whether you continue to need it, and so you keep it forever unless you free() or delete it. This is a “memory leak”. Memory management is avoiding memory leaks.

If malloc() fails to obtain this memory, it returns NULL; if new fails, it throws an exception. The latter is actually preferable, since at least the program will always crash….either way, you should make sure you got the memory you asked for before trying to do anything with it.

And now we come to point 4. You know why to allocate and you know how to do it safely at both ends of the process. You still have memory leaks, though, because if you write the following kind of code:

class C
{
    int *x;
    C(int *y) : x(y) {}
};

(in C++, of course) you will have to contend with all the combinations of the following situations:

  1. The variable y is persistent or temporary;
  2. The space *y is automatic or static or dynamic.

If y is persistent, it means that the constructor was called somehow like int i = 1; C obj(&i);; that is, that the argument to the constructor has an outside existence. In that case, one should not try to memory-manage y: it is handled elsewhere. The best case for everyone is that it is automatic, handled by the compiler, but even if it is dynamic (that is, allocated by malloc() or new) it is someone else’s responsibility.

If it’s temporary, that means that the code was more like C obj(f());, where f() is some function that returns a pointer. That pointer only exists “in transit” and is unnamed, so this return value may very well be the only place in the whole program where it is mentioned. If so, we must try to memory-manage it.

Alas, there are two possibilities: the first is that f() returns a pointer to static data, meaning some block of memory reserved at compile time inside the function. Obviously, this should not be freed. But it may have been dynamically allocated as well, in which case, since this is the only mention of that return value, it must be freed (either now or at some future time).

The irritation here is that all of these are implicit in the single usage C obj(f());. There is no way to tell, just from the code, whether that return value needs managing or not. It gets even worse if the function is passing around references to a single piece of dynamic memory which someone needs to free, but no one knows that they are the ones to do it.

When I got to this point in my own mind, I realized that one should never pass around pointers in C++, and always use some variety of smart pointer that does garbage collection on itself, so that the author of class C does not need to reinvent some ad-hoc way of passing the information about whether certain data is in need of management.

I am a little frustrated with any and all of the C++ books I’ve read for not making explicit that one should not actually use the built-in pointer type except as part of a low-level implementation detail. Pointers are hardware data: they refer to a place on the RAM chip (or at least, what the OS tells you is the RAM chip), and that’s one way of obtaining indirection in your code, but since C++ has references, it’s not the best way (never mind that references are usually disguised pointers: that’s a low-level implementation detail). C++ has so much structure that actually using hardware data is a blemish, except of course if (by performing explicit memory management) you are trying to manipulate the hardware. Pointers should always be attached to dynamic variables, since with references there is no need to point at automatic ones.

Advertisements
This entry was posted in C++, Computers, Programming. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s