When working on C++ projects, memory leaks are an issue that almost every developer encounters. A program that runs well in a testing environment may suddenly experience a memory spike or even an OOM (Out of Memory) error when deployed online, only to find after extensive troubleshooting that resources were not released.
I previously encountered a similar scenario in a project: a service running for a long time had increasing memory usage, and a restart was needed to alleviate the issue. Ultimately, I was able to pinpoint the problem through tools and code modifications. Here, I summarize several common detection methods, namely Valgrind, AddressSanitizer (ASan), and smart pointer replacement.
📚 The C++ Knowledge Base has been launched on ima! The current content covered by the knowledge base is shown in the image below:

📌 Students interested in the knowledge base can add the assistant on WeChat (chuzi345) with the note 【Knowledge Base】 or click 👉 C++ Knowledge Base (tap to jump) to view the complete introduction to the knowledge base~
1. Tracking Leaks with Valgrind
Valgrind is a commonly used memory checking tool on Linux, especially suitable for detecting issues like “who allocated but did not free”.
Assuming there is a piece of code that has a leak:
#include <iostream>
void leak() {
int* p = new int(42);
// Forgot to delete
}
int main() {
leak();
return 0;
}
Compile and run:
g++ -g leak.cpp -o leak
valgrind --leak-check=full ./leak
The output will indicate:
==12345== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x4C2FB55: operator new(unsigned long) (vg_replace_malloc.c:422)
==12345== by 0x10914A: leak() (leak.cpp:5)
You can see that the allocation point in <span>leak()</span> is directly marked. The advantage of Valgrind is that the information is intuitive and the positioning accuracy is high, but the downside is that it has a large performance overhead, and programs run slower in its simulated environment. It is suitable for testing or local debugging, but not for online use.
2. AddressSanitizer (ASan)
If the project uses GCC or Clang, you can directly enable the compilation option:
g++ -fsanitize=address -g leak.cpp -o leak
./leak
The output will be similar to:
==12346==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f3cbb4b7bc8 in operator new(unsigned long) ...
#1 0x10914A in leak() leak.cpp:5
Compared to Valgrind, ASan is lighter and runs at speeds close to native programs, making it suitable for daily development. However, it also has drawbacks:
- The report format is not as detailed as Valgrind’s
- For large projects, it may introduce additional compilation and runtime overhead
In my personal experience, if the project can switch compilation options, ASan is the most convenient method for daily checks. In performance-sensitive online environments, it can be considered to enable it only during the testing CI phase.
3. Smart Pointer Replacement
Many memory leak issues are actually due to human oversight: writing new but forgetting to delete, or prematurely exiting a function with return leading to resources not being released. In such cases, directly modifying the code to introduce smart pointers can often fundamentally avoid the problem.
For example:
#include <iostream>
#include <memory>
struct Node {
int value;
Node(int v) : value(v) {}
};
int main() {
std::unique_ptr<Node> p = std::make_unique<Node>(42);
std::cout << p->value << std::endl;
// Automatically released at the end of scope
}
Here, std::unique_ptr replaces raw pointers, and the destructor will be automatically called at the end of the scope. If multiple objects need to be shared, std::shared_ptr can be used:
#include <iostream>
#include <memory>
struct Node {
int value;
Node(int v) : value(v) {}
};
int main() {
std::shared_ptr<Node> p1 = std::make_shared<Node>(42);
std::shared_ptr<Node> p2 = p1; // Reference count +1
std::cout << p1->value << std::endl;
}
However, it is important to note the issue of circular references, which can be broken using std::weak_ptr.
In actual projects, I usually prioritize replacing resource management areas with smart pointers when refactoring legacy code. This modification is more thorough than relying solely on tools and results in lower maintenance costs in the future.
4. Summary of Engineering Practices
When checking for memory leaks in a project, a combination approach can be taken:
- Local Debugging: First use Valgrind or ASan to run through and see if there are any obvious leak points.
- Daily Development: Use smart pointers whenever possible; RAII is the most worry-free method.
- CI Phase: Enable ASan for a check to catch issues before going live.
Sometimes, online memory anomalies are not necessarily leaks; they may also be due to caches not being released in time or fragmentation at the system call level. Such cases require analysis in conjunction with pmap, /proc, and performance sampling tools.
Resolving memory leak issues can sometimes be very labor-intensive, but as long as you have tools and good coding habits, you won’t fall into a headless chicken style of troubleshooting. From practical experience, tool positioning + smart pointer modification is the most effective approach. Looking back after stumbling through many pitfalls, many bugs could have been avoided during the development phase.
Recommended Reading:
C++ Direct Access to Major Companies (For students interested in the training camp, you can read this article to learn about the details of the training camp, or add WeChat: chuzi345 to quickly understand the training camp related information)
How to Configure an Efficient C++ Development Environment from Scratch (VSCode + CMake + gdb)
Essential for C++ Development: From Zero to Proficient in Using Git to Manage Projects