Memory leaks in C/C++

Matthew Fisher
4 min readAug 2, 2016

C and C++ have a serious flaw in heap management. The whole concept of programmer driven, globally managed heap is broken. The design of malloc libraries have lead to countless hours of debugging and billions of dollars of loses. Random heap corruption is the worst issue but Memory Leaks are a major irritant as well.

Before digging in, lets define the parts. The heap is a large block of memory that may be used dynamically during a process lifetime. Allocations in the heap are variable sized and may be non-sequential. Unlike the stack, the heap may become fragmented as interior blocks are deallocated.

In C, blocks in the heap are allocated with the malloc library. The ‘new’ in C++ function is typically a wrapper on malloc with some additional code to call the constructor. The allocation strategy is actually fine. The compiler handles the details so it is hard to screw up a call to malloc/new.

A memory leak occurs when the last pointer to a block in the heap is lost before free or delete is called. Overtime, a leak will cause a program to run out of memory and/or start page swapping. Leaks can be very hard to find as there is no immediate consequence.

A big problem is ownership. When a block is allocated, the programmer must understand the deallocation strategy. This maxim must be true every time. Memory Leaks are generally created when this rule is broken. If ownership is unclear then the block will be leaked or worst deallocated twice.

Understanding pointer ownership is the key to avoiding memory leaks. There are several cases for ownership, lets review them.

‘In function’ ownership is the simplest. The programmer just needs to call the delete at the end of the function/method.

int foo()

{

Object* obj = new Object();

// other stuff …

delete obj;

}

Loop ownership gets a bit more complicated. In the fragment below a leak will occur when an error is encountered.

int foo()

{

Object * obj = NULL;

while (true)

{

obj = new Object;

if(error)

break;

delete obj;

}

Return value ownership can be more complicated. The value can be returned via parameter or return value.

Object * foo(Object* & returnObj)

{

Object* obj = new Object();

returnObj = new Object();

return obj;

}

void main()

Object * otherObj = NULL;

Object* localObj = foo(otherObj);

return; // both objects leaked

In both of these cases, the ‘main’ function must deallocate the objects at some point. This case is probably the most common cause of leaks. Oftentimes the function is in a library and the programmer doesn’t clearly understand the ownership situation.

A smaller but common case is a complex ctor/dtor combination.

class Object

{

public:

Object() { subObject = new SubObject; }

~Object() { };

private:

SubObject* subObject;

};

In this case, subObject will leak every time the destructor is called for an Object instance. The error is obvious in this case but real world examples can be confounding in complexity.

Another common case for memory leaks is multiple execution paths. In complex code, the deallocation of a block may be inadvertently skipped. Error handling is often involved in this case. If the error is very uncommon then only a small leak will result. A high volume system can exhaust or overtax memory resources in just a few minutes however in a repetitive error case.

For complex code that has poor Cohesion, leaks via multiple execution paths can be a major problem to find and fix. Error cases must be carefully evaluated to avoid this situation. The various ‘smart’ classes can help but are no silver bullet.

In complex programs finding memory leaks via code inspection is basically hopeless. An automated tool like Dynamic Memory Solutions Leak Check is a must. The tool should be used in Unit Test cases and on full end to end testing. This step is not taken usually and leaks show up in production environments.

There is a computer science concept called a Memory Pool. In a Memory Pool, allocation is done as a heap but deallocation is done all at once.

Structuring your C++ classes to allocate from a Memory Pool can be a great way to avoid leaks. This idea works if a bunch of object are created, used and then can be destroyed as a group. The key is that a pointer in Memory Pool is always maintained until the objects are destroyed.

For C, it is even easier since no dtors need be called. In this case, the programmer can allocate from the pool but does not call a deallocate routine. Once the pool is ready to be discarded, a single call free’s all the blocks at once.

The simplest strategy to avoid leaks is to not directly use the heap. The are a variety of strategies in modern C++, using the STL and templates, that minimize heap usage. These ideas are worth exploring in detail but are not a complete solution.

Memory Leaks in C/C++ are a real irritant. Use the techniques described here and your team can mitigate the problem.

If you found this article helpful then please hit the heart button below to promote it.

--

--