Finding C++ Memory Leaks: Valgrind, AddressSanitizer, and LeakSanitizer

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

  1. What is a leak?
  2. Five major causes
  3. Valgrind
  4. AddressSanitizer
  5. Visual Studio heap
  6. Ten patterns
  7. RAII
  8. Production monitoring
  9. 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)

  1. new/delete mismatch or missing delete
  2. Exception paths skipping manual cleanup
  3. shared_ptr cycles
  4. Containers of raw owning pointers without cleanup
  5. Singletons with raw new and 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)

  1. Raw new without delete
  2. Exception before deleteRAII
  3. new[] paired with wrong delete
  4. vector<T*> without deleting elements
  5. shared_ptr cycleweak_ptr
  6. Conditional delete paths
  7. Rule-of-five violations (double free / leak)
  8. Factory returning raw owning pointer without documented ownership
  9. Globals with manual new
  10. Thread stacks leaking thread-local new

7. RAII

Acquire in constructor / release in destructorunique_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

ToolPlatformSpeedRebuild
ValgrindLinuxSlowNo
ASan/LSanMany~2×Yes
VS heapWindowsFastDebug helpers

Rules

  1. Prefer make_unique / make_shared over raw new.
  2. Containers should usually own values or smart pointers, not raw pointers.
  3. Break shared_ptr cycles with weak_ptr.
  4. Run ASan/Valgrind in CI on tests.

  • 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.