Top Up Prev Next Bottom Contents Index Search

2.7 Preventing Memory Leaks in C++ Code

Memory leaks occur when new memory is allocated dynamically and never deallocated. In C programs, new memory is allocated by the malloc or calloc functions, and deallocated by the free function. In C++, new memory is usually allocated by the new operator and deallocated by the delete or the delete [] operator. The problem with memory leaks is that they accumulate over time and, if left unchecked, may cripple or even crash a program. We have taken extensive steps to eliminate memory leaks in the Ptolemy software environment by following the guidelines below and by tracking memory leaks with Purify (a commercial tool from Pure Software Inc.).

One of the most common mistakes leading to memory leaks is applying the wrong delete operator. The delete operator should be used to free a single allocated class or data value, whereas the delete [] operator should be used to free an array of data values. In C programming, the free function does not make this distinction.

Another common mistake is overwriting a variable containing dynamic memory without freeing any existing memory first. For example, assume that thestring is a data member of a class, and in one of the methods (other than the constructor), there is the following statement:

thestring = new char[buflen]; This code should be

delete [] thestring;
thestring = new char[buflen];
Using delete is not necessary in a class's constructor because the data member would not have been allocated previously.

In writing Ptolemy stars, the delete operator should be applied to variables containing dynamic memory in both the star's setup and destructor methods. In the star's constructor method, the variables containing dynamic memory should be initialized to zero. By freeing memory in both the setup and destructor methods, one covers all possible cases of memory leaks during simulation. Deallocating memory in the setup method handles the case in which the user restarts a simulation, whereas deallocating memory in the destructor covers the case in which the user exits a simulation. This includes the cases that arise when error messages are generated. For an example implementation, see the implementation of the SDFPrinter star given in Section 2.6.

Another common mistake is not paying attention to the kinds of strings returned by functions. The function savestring returns a new string dynamically allocated and should be deleted when no longer used. The expandPathName, tempFileName, and makeLower functions return new strings, as does the Target::writeFileName method. Therefore, the strings returned by these routines should be deleted when they are no longer needed, and code such as

savestring( expandPathName(s) ) is redundant and should be simplified to

expandPathName(s) to avoid a memory leak due to not keeping track of the dynamic memory returned by the function savestring.

Occasionally, dynamic memory is being used when instead local memory could have been used. For example, if a variable is only used as a local variable inside a method or function and the value of the local variable is not returned or passed to outside the method or function, then it would be better to simply use local memory. For example,

char* localstring = new char[len + 1];
if ( person == absent ) return;
strcpy(localstring, otherstring);
delete [] localstring;
return;
could easily return without deallocating localstring. The code should be rewritten to use either the StringList or InfString class, e.g.,

InfString localstring;
if ( person == absent ) return;
localstring = otherstring;
return;
Both StringList and InfString can manage the construction of strings of arbitrary size. When a function or method exits, the destructors of the StringList and InfString variables will automatically be called which will deallocate their memory. Casts have been defined that will convert StringList to a const char* string and InfString to a const char* or a char* string, so that instances of the StringList and InfString classes can be passed as is into routines that take character array (string) arguments. A good example of using the StringList class is in the function compile in the file $PTOLEMY/src/pigilib/pigiLoader.cc. A simpler example from the same file is the noPermission function which builds up an error message into a single string:

StringList sl = msg;
sl << file << ": " << sys_errlist[errno];
ErrAdd(sl);
The errAdd function takes a const char* argument, so sl will converted automatically to a const char* string by the C++ compiler.

Instead of using the new and delete operators, it is tempting to use constructs like

char localstring[buflen + 1]; in which buflen is a variable, because the compiler will automatically handle the deallocation of the memory. Unfortunately, this syntax is a Gnu extension and not portable to other C++ compilers. Instead, the StringList and InfString classes should be used, as the previous example involving localstring illustrates.

Sometimes the return value from a routine that returns dynamic memory is not stored, and therefore, the pointer to the dynamic memory gets lost. This occurs, for example, in nested function calls. Code such as

puts( savestring(s) ); should be written as

const char* newstring = savestring(s);
puts( newstring );
delete [] newstring;
Several places in Ptolemy, especially in the schedulers and targets, rely on the hashstring function, which returns dynamic memory. This dynamic memory, however, should not be deallocated because it may be reused by other calls to hashstring. It is the responsibility of the hashstring function to deallocate any memory it has allocated.



Top Up Prev Next Bottom Contents Index Search

Copyright © 1990-1997, University of California. All rights reserved.