Finding C++ Memory Leaks: Valgrind, AddressSanitizer, and LeakSanitizer
이 글의 핵심
Practical leak hunting: Valgrind vs ASan trade-offs, environment variables, CRT leak checks on Windows, RAII fixes, and production monitoring ideas.
Tooling: Valgrind · ownership in C++: smart pointers · Rust contrast: ownership.
Introduction: “Memory grows until OOM”
“The server runs for days, then dies”
A memory leak means you lose the last pointer to heap memory without freeing it. Long-running services eventually OOM.
void handle() {
int* p = new int[1000];
// forgot delete[]
}
This article covers:
- Five major leak causes
- Valgrind (Linux)
- AddressSanitizer + LeakSanitizer
- Visual Studio / CRT helpers
- Ten leak patterns and RAII fixes
- Production monitoring ideas
Table of contents
- What is a leak?
- Five major causes
- Valgrind
- AddressSanitizer
- Visual Studio heap
- Ten patterns
- RAII
- Production monitoring
- Summary
1. What is a memory leak?
Heap memory no longer reachable from any pointer in the program → cannot be freed → grows until limits.
Contrast with intentional growth (e.g. a cache) where memory is still reachable.
2. Five major causes (overview)
- new/delete mismatch or missing
delete - Exception paths skipping manual cleanup
shared_ptrcycles- Containers of raw owning pointers without cleanup
- Singletons with raw
newand no teardown
3. Valgrind
g++ -g -std=c++17 -o myapp main.cpp
valgrind --leak-check=full --show-leak-kinds=all ./myapp
Interpret definitely lost vs still reachable (globals may be “reachable” at exit).
4. AddressSanitizer / LeakSanitizer
g++ -g -fsanitize=address -std=c++17 -o myapp main.cpp
export ASAN_OPTIONS=detect_leaks=1
./myapp
Visual Studio: enable AddressSanitizer in project settings (version-dependent).
5. Visual Studio / CRT
_CrtSetDbgFlag with _CRTDBG_LEAK_CHECK_DF in debug builds for leak dumps on exit.
6. Ten patterns (titles)
- Raw
newwithoutdelete - Exception before
delete→ RAII new[]paired with wrongdeletevector<T*>without deleting elementsshared_ptrcycle →weak_ptr- Conditional
deletepaths - Rule-of-five violations (double free / leak)
- Factory returning raw owning pointer without documented ownership
- Globals with manual
new - Thread stacks leaking thread-local
new
7. RAII
Acquire in constructor / release in destructor — unique_ptr, vector, file handles, locks.
8. Production monitoring
/proc/self/status VmRSS on Linux, custom allocation counters (carefully—global operator new hooks affect everything), periodic sanity checks against baselines.
Summary
Tool comparison
| Tool | Platform | Speed | Rebuild |
|---|---|---|---|
| Valgrind | Linux | Slow | No |
| ASan/LSan | Many | ~2× | Yes |
| VS heap | Windows | Fast | Debug helpers |
Rules
- Prefer
make_unique/make_sharedover rawnew. - Containers should usually own values or smart pointers, not raw pointers.
- Break
shared_ptrcycles withweak_ptr. - Run ASan/Valgrind in CI on tests.
Related posts (internal)
- Memory leaks (series)
- RAII
- Smart pointers
- Circular references
Keywords
memory leak, Valgrind, AddressSanitizer, LeakSanitizer, RAII, new delete
Practical tips
- Enable ASan on developer builds by default.
- Add Valgrind stage for release candidates on Linux.
- Review any returning raw owning pointer APIs.
Closing
Leaks are common but preventable: RAII, smart pointers, no cycles, and automated leak checks in CI.
Next: Use-after-free and deeper Valgrind articles.