C++ Memory Leak Detection Fix
In this tutorial, you'll learn about C++ Memory Leak Detection Fix. We cover key concepts, practical examples, and best practices.
The Problem
Your C++ program's memory usage grows continuously:
$ ./my-server
PID USER RSS COMMAND
12345 user 256M my-server
# 30 minutes later
12345 user 2G my-server
The program allocates memory but never frees it. Over time, the process consumes all available memory and may be killed by the OOM killer.
Quick Fix
Step 1: Detect leaks with Valgrind
g++ -g -o myprogram myprogram.cpp
valgrind --leak-check=full --show-leak-kinds=all ./myprogram
==12345== 40 bytes in 1 blocks are definitely lost in loss record 1 of 5
==12345== at 0x4842A73: operator new(unsigned long) (vg_replace_malloc.c:342)
==12345== by 0x1091A3: main (myprogram.cpp:8)
Each definitely lost block is a memory leak. The stack trace shows exactly where the allocation happened.
Step 2: Fix missing delete
WRONG -- allocated but never freed:
void process() {
int* data = new int[1000];
// use data...
// forgot to delete[] data
}
RIGHT -- free with matching delete:
void process() {
int* data = new int[1000];
// use data...
delete[] data; // must match new[]
}
Or better, use std::vector:
void process() {
std::vector<int> data(1000);
// use data...
// automatically freed when data goes out of scope
}
Step 3: Fix missing virtual destructors
WRONG -- base class without virtual destructor:
class Base {
public:
~Base() {} // non-virtual
};
class Derived : public Base {
int* data = new int[100];
public:
~Derived() { delete[] data; }
};
Base* obj = new Derived();
delete obj; // only calls ~Base(), not ~Derived()
RIGHT -- virtual destructor ensures proper cleanup:
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {
std::vector<int> data; // RAII handles cleanup
};
Step 4: Fix circular shared_ptr references
WRONG -- circular reference prevents deallocation:
struct Node {
std::shared_ptr<Node> next;
};
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->next = b;
b->next = a; // circular -- both ref counts stay at 1
RIGHT -- use weak_ptr to break the cycle:
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // weak_ptr does not increase ref count
};
Step 5: Use AddressSanitizer for leak detection
g++ -g -fsanitize=address -fno-omit-frame-pointer -o myprogram myprogram.cpp
./myprogram
ASan reports leaks at program exit:
==12345==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f1234567890 in operator new(unsigned long)
#1 0x55d8b7c3c123 in main /home/user/myprogram.cpp:8
Step 6: Fix file handle and resource leaks
WRONG -- file handle not closed on error path:
FILE* f = fopen("data.txt", "r");
if (!readData(f)) return; // early return without fclose
fclose(f);
RIGHT -- use RAII wrappers:
std::ifstream f("data.txt");
if (!readData(f)) return; // f is closed automatically when it goes out of scope
Use DodaTech's Memory Profiler to track heap allocations in real time and detect growing memory usage patterns before they cause OOM kills.
Prevention
- Prefer RAII containers (
std::vector,std::string,std::unique_ptr) over raw pointers. - Never use
new/deletedirectly -- usestd::make_uniqueandstd::make_shared. - Run Valgrind or ASan in CI/CD on every commit.
- Use
weak_ptrto break circular references in shared ownership. - Set memory limits with
setrlimit(RLIMIT_AS, ...)for long-running services. - Use
unique_ptrfor exclusive ownership by default; only useshared_ptrwhen ownership must be shared.
Common Mistakes with memory leak
- Using
returnto exit a function early instead of wrapping a pure value in the monad - Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
These mistakes appear frequently in real-world CPP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro