Node: Memory Leaks, Next: , Previous: Programming Cautions, Up: Programming Cautions

Memory Leaks

Infamous to C and C++ are those annoying memory leaks. In Lisp a programmer does not have to worry about memory allocation but it is a different story in C++. When a program allocates memory and neglects to deallocate the memory when it is no longer needed then a potential memory leak exists. If a program has a memory leak in some code and then loops on that code then eventually the program will use up available memory and break with a memory allocation error.

In C++ one must either link with one of the many automatic garbage collector libraries or else each function must make sure which data it owns and manually deallocate the data's memory when it is no longer referenced. Lpp provides some manual garbage collection functions which help See Garbage Collection. But the programmer must still be aware of how potential memory leaks can occur.

All Lpp data is allocated from the heap and not the stack. So no Lpp data is automatically deallocated by virtue of exiting a function. Symbols and character objects though are not problematical. There is only one set of character objects that are shared by all programs. And symbols once interned are also shared by all programs. So for example in the following code

     list1 = list(S(sym1), S(sym1), S(sym1))

the symbol sym1 will only get allocated on its first internment. Or in other words, in the case of list1 the first S(sym1) will intern the symbol sym1 and allocate it (assuming it has not been interned previously) and then return the symbol. The second and third S(sym1) then will only return the already interned symbol sym1.

Usually the programmer will never have a need to deallocate symbols since the nature of their semantics is to be persistent within the full extent of the program. Symbols can be deallocated with unintern if it is absolutely necessary See Creating Symbols.

To be safe then, all other Lpp objects should be garbage collected when known to be no longer referenced or else simulate the Symbol semantics. For example if in a function, we reference a commonly used string like the following

     let b = stringConcat(a, L("-something-common"));

and that function can be called repeated times, then the string should either be made static

     static let common_string = L("-something-common");
     let b = stringConcat(a, common_string);

or alternatively it can be manually garbage collected

     let common_string = L("-something-common");
     let b = stringConcat(a, common_string);

otherwise there will be no reference to such strings allocated and when the function exits the string objects would exist in memory until the program exits.

The manual garbage collection in this last case is a bad choice since we know that the same string will be used over and over again. The static variable containing the common string would be a better choice. But in a case where there is an intermediate value whose value we can not predict then a manual garbage collection would be more appropriate. The following for example would be a such a memory leak

     let a = minus(d, times(b, c));

where b, c and d are input variables. The memory leak would be where the times returns a value. That value is not referenced anywhere after the minus is executed. So to prevent a memory leak here this should be coded instead as

     let temp = times(b, c);
     let a = minus(d, temp);

The kinds of potential memory leaks mentioned above are relatively simple to find and fix. But in the real world where large complex data structures are passed around, memory leak bugs can be quite difficult to pin down. It is a classical problem of which function owns what data when. Lpp provides some assist to this by making it easier to sweep up large branches of data that are no longer referenced with functions like gcList and gcTree but the programmer must carefully design the growth and collapse of such complex structures so as to avoid memory leaks.