Skip to content

C++ Memory Leak Detection Fix

DodaTech Updated 2026-06-24 4 min read

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/delete directly -- use std::make_unique and std::make_shared.
  • Run Valgrind or ASan in CI/CD on every commit.
  • Use weak_ptr to break circular references in shared ownership.
  • Set memory limits with setrlimit(RLIMIT_AS, ...) for long-running services.
  • Use unique_ptr for exclusive ownership by default; only use shared_ptr when ownership must be shared.

Common Mistakes with memory leak

  1. Using return to exit a function early instead of wrapping a pure value in the monad
  2. Mixing let bindings with <- bindings in do notation, producing type errors
  3. 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

### What is the difference between "definitely lost" and "possibly lost" in Valgrind?

definitely lost means Valgrind found a pointer to the allocated memory was overwritten without freeing it -- a guaranteed leak. possibly lost means the pointer may still exist but Valgrind cannot prove it is reachable.

Can a memory leak crash my program even before OOM?

Yes. Memory fragmentation can cause malloc or new to fail even when total free memory appears sufficient. This leads to std::bad_alloc exceptions and program crashes.

Does RAII guarantee no memory leaks?

RAII (Resource Acquisition Is Initialization) prevents leaks when used correctly. If all resources are wrapped in RAII classes that manage their own cleanup, leaks are virtually eliminated. The exception is circular shared_ptr references, which require weak_ptr.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro