Home

 › 

Articles

 › 

What Is a Memory Leak, and How Do I Avoid It?

What Is a Memory Leak, and How Do I Avoid It?

A memory leak occurs when a program allocates system memory but does not use it or free it for use for another application. At a minimum, this will be frustrating for the user whose limited system resources are being hogged by your script. However, depending on your allocated storage type, this could seriously threaten the user’s computer health. Let’s examine this error and how to avoid it.

Types of Memory Leaks

From a memory use standpoint, the three types of memory leaks are determined by the kind of storage allocated. They have different impacts on the computer’s overall performance and present unique problems for the system. Let’s take a look at how they differ.

Memory leaks can occur in any program, in any language. They’re hard to avoid completely, even for the most skilled programmers. So, don’t be too hard on yourself if your code causes a minor leak. As long as it’s short-lived, your users won’t experience a major performance degradation from it. However, if your code is found to cause serious issues, that’s something that needs to be fixed immediately.

Type of Memory LeakImpact on the System Performance
Short Lived User-land Expansion ApplicationThese leaks consume a portion of the user’s RAM. However, modern operating systems will recollect the memory allocated after application termination. Thus, they have minimal effect on the computer’s overall performance.
Long Lived User-land Expansion ApplicationThis leak type can be frustrating since the operating system will not recollect this memory even after closing the application. Several of these leaks will eventually consume large amounts of the system’s RAM, causing serious performance issues.
Kernel-land ProcessKernel memory is extremely limited. Thus, this leak type is hazardous to the system’s overall performance.

Long-term and kernel leaks don’t just cause your users’ systems to run slowly. They can also lead to security threats and denial of service by crashing applications. 

Memory Leaks from a Code Standpoint

C++ language
Widely speaking, there are 19 categories of memory leaks in C++.

From a programming standpoint, there are 19 broad categories of memory leaks. Let’s examine the different ways you can leak memory in your code. It’s important to remember that these code examples are theoretical and may not run if put through a compiler. They are written in C++.

Leaked Temporary Workspace

HANDLE createExampleHandle(DWORD	id)
{
	char	*variable;
	HANDLE	handle = NULL;
	variable = new char [100];
	if (variable != NULL)
	{
		sprintf(variable, "workstation%d", id);
		handle = createHandle(variable);
	}
	return handle;
}

This leak type occurs when memory is allocated within a class or function and is not deallocated following its end. In this code block, “variable” allocates RAM but does not free it following function termination.

Leaked Data Member

class exampleHandle
{
public:
	exampleHandle();
	~exampleHandle();
	void setName(char	*variable);
	void createExampleHandle();
private:
	HANDLE	handle;
	char	*variable;
};
exampleHandle::exampleHandle()
{
	variable = NULL;
	handle = NULL;
}
exampleHandle::~exampleHandle()
{
	if (handle != NULL)
		CloseHandle(handle);
}
void example::setName(char	*p_variable)
{
	size_t	len;
	len = strlen(variable);
	variable = new char [len + 1];
	if (variable != NULL)
	{
		strcpy(varibale, p_variable);
	}
}
void commsHandle::createCommsHandle()
{
	HANDLE	handle = NULL;
	handle = createHandle(name);
	return handle;
}

This script creates two leaks within the class. The first one is created when the destructor doesn’t deallocate the memory for setName(). Further, setName() doesn’t release the allocated RAM when it sets a new value for the name.

Leaked Static Data Member

class exampleHandle
{
public:
	exampleHandle();
	~exampleHandle();
	static void setName(char	*p_variable);
	void createExampleHandle();
private:
	HANDLE		handle;
	static char	*name;
};
commsHandle::commsHandle()
{
	handle = NULL;
}
commsHandle::~commsHandle()
{
	if (handle != NULL)
		CloseHandle(handle);
}
void commsHandle::setName(char	*p_name)
{
	size_t	len;
	len = strlen(p_name);
	name = new char [len + 1];
	if (name != NULL)
	{
		strcpy(name, p_name);
	}
}
void commsHandle::createCommsHandle()
{
	HANDLE	handle = NULL;
	handle = createHandle(name);
	return handle;
}

In this example, memory is allocated for use by several functions of a class but shared over all instances. The program does not deallocate the storage reserved by setName(). Additionally, there is no function to clean up the RAM used, and it is not possible to call a function to do that.

Global Memory Leak

char *variable;
void setName(char	*p_variable)
{
	size_t	len;
	len = strlen(p_variable);
	variable = new char [len + 1];
	if (variable != NULL)
	{
		strcpy(variable, p_variable);
	}
}

In this example, memory is allocated for several functions while not being part of a class. It is not deallocated following the functions’ termination.

Static Memory Leak

void doWork()
{
	static	char	*variable = NULL;
	if (variable == NULL)
	{
		variable = new char [1000];
	}
	...
}

This example is quite similar to the prior example. However, the memory’s scope is limited to the function it was allocated to. The primary issue with this reservation type is that there is no simple way to deallocate it if it’s only meant to be held on the first call, as detected by the if(variable  == NULL) comparison.

Lifetime Memory Leak

static char *exampleChar = NULL;
char *getExampleChar()
{
	return exampleChar();
}
int main(int  argc,
	 char *argv[])
{
	exampleChar = new char [1000];
	doWork();
}

This type of leak usually starts as intentional but can quickly become problematic if overused. In this situation, the developers allocate memory intending for it to remain reserved because they think it may become useful at some point in the program’s execution.

There are many reasons a development team might have this occur. One reason is when an application is multi-threaded. Thus, the memory is shared between threads, and, for some reason, the developers do not clean up their RAM usage.

They may have also created a workspace for debugging and injected it into the code. In cases where the debugger intends to report data as far into the program’s shutdown process as possible, it may not clean up its memory usage as effectively.

Worker Object Leak

Code on Thread 1

static char *exampleChar = NULL;
char *getExampleChar()
{
	return exampleChar();
}
int main(int  argc,
	 char *argv[])
{
	exampleChar = new char [1000];
	doWork();
}

Code on Thread 2

void processQueue()
{
	CCriticalSection	lock(§, TRUE);
	DWORD			i, n;
	n = dataItems.GetSize();
	for(i = 0; i < n; i++)
	{
		workerData *data; 
		data = dataItems.GetAt(i); 
		if (data != NULL) 
		{
			data->doWork();
		}
	}
	dataItems.RemoveAll();
}

In this example, we have two code blocks, each running on different threads. The first script allocates memory but does not deallocate it because it expects the second part of the program to do so. However, since the latter doesn’t release it either, it creates a leak.

Deleting the Incorrect Array

void processQueue()
{
	CCriticalSection	lock(§, TRUE);
	DWORD			i, n;
	n = dataItems.GetSize();
	for(i = 0; i < n; i++)
	{
		workerData *data; 
		data = dataItems.GetAt(i); 
		if (data != NULL) 
		{
			data->doWork();
		}
	}
	dataItems.RemoveAll();
}

You can allocate and deallocate arrays of objects in C++. In this case, the memory delete function is for an array but doesn’t refer to an array; it refers to a simple, intrinsic data type. While this may not cause any severe issues on its own, if someone were to modify exampleChar into a more complex data type, it could cause a memory leak.

Virtual Objects

class example
{
public:
	example()
	~example();
};
example::example()
{
	...
}
example::~example()
{
	...
}
class exampleA : public example
{
public:
	exampleA();
	~exampleA();
	void addExampleInt(int quantity);
private:
	type	*exampleInt;
	DWORD	amount;
};
exampleA::exampleA()
{
	amount = 100;
	exampleInt = NULL;
	addExampleInt(amount);
}
exampleA::~exampleA()
{
	delete [] exampleInt;
}
void exampleA::addExampleInt(int quantity)
{
	amount += quantity;
	delete [] exampleInt;
	exampleInt = new [amount] type;
}
class exampleB : public train
{
public:
	exampleB();
	~exampleB();
	void powerOn();
	void powerOff();
private:
	typeB *exampleIntB;
};
exampleB::exampleB()
{
	exampleIntB = NULL;
}
exampleB::~exampleB()
{
	delete exampleIntB;
}
void exampleB::powerOn()
{
	delete exampleIntB;
	exampleIntB = new typeB();
}
void exampleB::powerOff()
{
	delete exampleIntB;
	exampleIntB = NULL;
}

Now, for this example, we’re going to need some object definitions like the following:

exampleObj	*exampleA = new exampleA();
	train	*exampleB = new exampleB();
	...
	delete exampleA;
	delete exampleB;

In this example, the destructor for the class exampleObj is called for both exampleA and exampleB. However, this causes a memory leak because the class was not declared virtual.

Calling the Wrong Memory Deallocator

char *exampleChar;
exampleChar = new char [100];
free(exampleChar);
HLOCAL local;
local = LocalAlloc(LMEM_FIXED, 1000:
GlobalFree(local);

In this case, the wrong memory deallocator was used. This can result in a number of different outcomes. Firstly, sometimes it just works, and the memory is released. Sometimes, the memory stays allocated, but it doesn’t affect the program. Other times, the application may see damage to heap data structures, which causes crashes. Finally, the script may just crash immediately. 

Using the Wrong Copy Operator

class example
{
public:
	example();
	~example();
	operator=(const example &other);
	void exampleA();	
private:
	Type	*data;
	DWORD			duration;
};
example::example()
{
	data = NULL;
	duration = 0;
}
example::operator=(const example &other)
{
	if (this != &other)
	{
		data = new Type(*data);
		duration = other.duration;
	}	
	return *this;
}
example::~example()
{
	exampleA();
}
void example::()
{
	delete data;
	data = NULL;
}

In this example, the existing object’s data is not deallocated before being copied to a new object, causing a memory leak.

Overwriting Data

memory leak
One common type of memory leak in C++ is caused by overwriting data.
class example
{
public:
	example();
	~example();	
	void exampleA();	
	void exampleB();
	void exampleC();		
	void setDuration(DWORD	d);
Private:
Type	*data;
	DWORD			duration;
};
example::example()
{
	data = NULL;
	duration = 0;
}

example::~example()
{
	exampleA();
}
void example::exampleA()
{
	delete data;
	data = NULL;
}
void example::exampleB()
{
	data = new Type();
}
void example::setDuration(DWORD	d)
{
	duration = d;
	if (d == 0)
		data = NULL;
}
void example::exampleC()
{
	if (data != NULL)
	{
		data->exampleD();
	}
	DWORD	i;
	i = 0;
	while(i < duration)
	{
		data->exampleE();
	}
	data->exampleF();
}

In this script, the pointer is overwritten with another valid pointer or with a NULL pointer, causing a memory leak.

Overwriting Data Via Reallocation

char *exampleChar;
exampleChar = (char *)malloc(1000);
if (exampleChar != NULL)
{
	memset(exampleChar, 0, 1000);
	exampleChar = (char *)realloc(exampleChar, hugeValue); 
	if (exampleChar != NULL)
	{
		memset(exampleChar, 2, hugeValue);
		free(exampleChar);
	}
}

In this case, the call for realloc() will fail if hugeValue is too big, causing a memory leak.

Implicitly Allocating Memory

DWORD	exampleError = GetExampleError();
	CString	errorMessageA;
	LPTSTR	lpMsgBuf;
	errorMessageA = _T("The gnomes running your program have escaped leaving behind:rn");
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
					FORMAT_MESSAGE_FROM_SYSTEM | 
					FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL,
					exampleError,
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
					(LPTSTR) &lpMsgBuf,
					0,
					NULL);
	errorMessageA += _T("rnWin32 Error:rnrn");
	errorMessageA += _T(""");
	errorMessageA += lpMsgBuf;
	errorMessageA.TrimRight();
	errorMessageA += _T(""");
	MessageBox(errorMessageA, _T(“Get Owned Nerd"), MB_ICONEXCLAMATION);
	return;

In this case, the memory leak happened because we called a function that reserves memory but didn’t deallocate it. It typically occurs because the method will only allocate memory in certain circumstances.

Early Return

DWORD doWork(int exampleInt,
             int exampleIntB)
{
	Type*	t;
	DWORD	value = 0;
	t = new Type();
	t->exampleA();
	fillType(t, exampleInt, exampleIntB);
	if (checkError(t))
		return value; 
	value = t->transform();
	delete t;	
	return value;
}

This example program is pretty similar to the first one on the list. The script returns early, and the memory doesn’t get deallocated because the developer (us, in this case) forgot to do so.

Using Continue

DWORD example::doWork()
{
	std::vector				exampleVar;
	std::vector::iterator	iter;
	getExampleVar(exampleVar);
	for(iter = exampleVar.begin(); iter != exampleVar.end(); iter++)
	{
		Type*	t;
		t = new Type();
		t->change(*iter);
		if (!t->valid())
			continue;
		add(t);
	}
}

This block is another iteration of the first example. In this case, the code loops back using the continue method but doesn’t clean up the memory allocation.

Using Break

DWORD exampleClass::doWork()
{
	std::vector				exampleVar;
	std::vector::iterator	iter;
	getExampleVar(exampleVar);
	for(iter = exampleVar.begin(); iter != exampleVar.end(); iter++)
	{
		Type*	t;
		t = new Type();
		t->change(*iter);
		if (!t->valid())
			break;
		runTests(t);
  		delete t;
	}
}

If you thought I couldn’t come up with another variation of the first example, you’d be wrong. This code is yet another iteration, and this time, we’re using “break” to create a memory leak by, once again, not cleaning up after ourselves.

Using Goto

DWORD doWork(int exampleInt,
             int ExampleIntB)
{
	Type*	t;
	DWORD	value = 0;
	t = new Type();
	t->example();
	fillType(t, exampleInt, exampleIntB);
	if (checkError(t))
		goto exit;
	value = t->transform();
	delete t;	
exit:
	return value;
}

“This looks a lot like the first example.” That’s because it is the first example. However, this time, we’re using “goto” to create a memory leak that could be solved by properly cleaning up our memory allocation.

Via Exception Handling

DWORD doWork(int exampleInt,
             int exampleIntB)
{
	Type*	t;
	DWORD	value = 0;
	t = new Type();
	t->example();
	fillType(t, exampleInt, exampleIntB);
	if (checkError(t))
		throw std::invalid_argument( "Get Owned Nerd" );
	value = t->transform();
	delete t;	
	return value;
}

This last script is also the first in a show of perfect cosmic symmetry. However, this time, we’ve created an exception that causes a memory leak.

Frequently Asked Questions

What is a memory leak?

A memory leak is when a program allocates RAM storage but does not free it up when it no longer needs it.

What are the three types of memory leaks?

The three types of memory leaks are short-lived user-land applications, long-lived user-land applications, and kernel-land applications.

What is the most dangerous type of memory leak?

Kernel-land application memory leaks are the most dangerous because kernel memory is limited. These types of memory leaks can cause serious system failures.

How do you solve a memory leak?

The exact methods you use to solve a memory leak are determined by the method you used to create it in the first place. However, as a general rule, properly cleaning up your memory allocation should solve most leaks.

What is the impact of a memory leak?

Memory leaks result in unusually high system resource usage. In the short term, this overuse can cause application failures, crashes, and denial of service. In the long term, they can end up causing the entire system to fail or represent a security threat.

What are the symptoms of memory leaks?

Typically, you can find a memory leak in any program that is using a strangely large amount of RAM. Since they are caused by a failure to deallocate storage that was reserved for a program, an application with a severe leak will utilize an excessive amount of computer memory.

How do I prevent memory leaks?

The easiest way to prevent memory leaks is to keep track of when you’re allocating RAM to your application and remembering to deallocate it. You’ll also need to ensure that you don’t utilize methods that would cause a leak before you free up the memory. Generally speaking, if you remember to clean up after yourself, you’ll prevent most incidents naturally.

What are some common types of memory leaks?

Common types of memory leaks include functions that return, break, or continue early without deallocating memory. Other common causes include ones from overwriting data, using the goto method, or creating exceptions.

To top