SoFunction
Updated on 2025-03-05

Summary of C++ memory leak detection and solution

Definition of memory leak

Memory leak refers to the phenomenon that the allocated memory space cannot be correctly released due to negligence or error during the operation of the program, which makes this part of the memory always occupied and cannot be recycled and reused by the operating system. In programming languages ​​such as C++, memory leaks may occur if you use dynamic memory allocation operations such as new or malloc, but forget to use delete or free to free memory.

The dangers of memory leaks

  • As the program run time increases, the available memory will gradually decrease, which may lead to system performance degradation and program response slowdown.
  • Eventually, it may exhaust the system's memory resources, crash the program or cause the entire system to fail.

Methods to detect memory leaks

  1. Manually check the code
    • Carefully review the use in the codenewnew[]mallocetc., to ensure that there is a corresponding part of the dynamic memory allocation when memory is no longer useddeletedelete[]orfreeoperate.
    • Pay attention to exception handling in the program to ensure that the allocated memory can also be released correctly when the exception occurs.
    • For complex programs, this approach can be difficult because memory leaks can be caused by a variety of factors.
  2. Usage Tools
    • Valgrind
      • This is a powerful open source tool, mainly used on the Linux platform, and can detect memory leaks and other problems in C and C++ programs.
      • For example, use thevalgrind --leak-check=full./your_programRun the program and it will generate a detailed memory usage report indicating which memory has not been released correctly.
    • AddressSanitizer
      • This is a compiler tool integrated in compilers such as GCC and Clang, and can be used to detect a variety of memory errors, including memory leaks.
      • Can be added at compile time-fsanitize=addressOptions, such asg++ -fsanitize=address -g your_program.cpp -o your_program. When running the program, information about memory errors is output.
    • Visual Studio Debugger
      • On Windows platforms, Visual Studio provides memory diagnostic tools.
      • When debugging a program, you can use the Diagnostic Tools window to view memory usage, which can detect memory leaks and provide detailed information.

How to solve memory leaks

  1. Correct use of memory management operators
    • In C++, make sure to usenewanddeleteAppear in pairs, usenew[]anddelete[]Appear in pairs.
    • Example:
#include <iostream>

int main() {
    int* ptr = new int;  // Allocate memory    // Use ptr pointer    delete ptr;  // Free memory    return 0;
}
  • For C, usemallocandfreeWhen  , you should also ensure that they are used correctly:
#include <>
#include <>

int main() {
    int* ptr = (int*)malloc(sizeof(int));  // Allocate memory    if (ptr == NULL) {  // Check whether the allocation is successful        perror("malloc failed");
        return 1;
    }
    // Use ptr pointer    free(ptr);  // Free memory    return 0;
}
  • Using smart pointers
  • In C++, use smart pointers (such asstd::unique_ptrstd::shared_ptrstd::weak_ptr) can automatically manage memory to avoid the hassle and possible omissions of manually freeing memory.

  • Example:

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(42);  // Use unique_ptr to automatically manage memory    // No need to delete manually    return 0;
}
  • std::unique_ptrThe memory pointed to will be automatically released in its destructor without explicit calls.delete
  1. Use RAII (Resource Acquisition Is Initialization) principle
    • Encapsulate the acquisition and release of resources in the constructor and destructor of the class, and use the life cycle of the object to manage resources.
    • Example:
#include <iostream>

class Resource {
private:
    int* data;
public:
    Resource() {
        data = new int[100];  // Allocate resources in the constructor    }
    ~Resource() {
        delete[] data;  // Free resource in destructor    }
};

int main() {
    Resource r;  // When r leaves scope, the destructor will be called automatically to free up resources    return 0;
}
  1. Memory pool technology

    • For frequent memory allocation and freeing operations, memory pools can be used to improve performance and avoid memory fragmentation.
    • The memory pool allocates a large piece of memory when the program starts. It is obtained from the pool when memory is needed, and when it is released, it returns the memory to the pool, avoiding frequent calls to the system's memory allocation and release functions.
  2. Avoid circular references

    • When using smart pointers, be careful to avoid circular references, especially when usingstd::shared_ptrhour.
    • Example:
#include <iostream>
#include <memory>

class A;
class B;

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() {
        std::cout << "A's destructor called" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> a_ptr;
    ~B() {
        std::cout << "B's destructor called" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;  // Circular references will cause memory leaks    return 0;
}
  • Can be usedstd::weak_ptrTo break the circular reference:
#include <iostream>
#include <memory>

class A;
class B;

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() {
        std::cout << "A's destructor called" << std::endl;
    }
};

class B {
public:
    std::weak_ptr<A> a_ptr;  // Use weak_ptr to avoid circular references    ~B() {
        std::cout << "B's destructor called" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
    return 0;
}

In this modified example,BIn the classa_ptrModified tostd::weak_ptr, avoids circular references, makingAandBThe object of  can be properly destroyed when it is no longer referenced.

Through the above methods, memory leak problems can be effectively detected and solved, ensuring the robustness and performance of the program.

What are the common situations that can cause memory leaks?

Here are some common situations that can cause memory leaks:

1. Forgot to free dynamically allocated memory

In usenewnew[](C++) ormalloccallocrealloc(C) After allocating memory, forget to use the correspondingdeletedelete[](C++) orfree(C) Free up memory.

// C++ examplevoid func() {
    int* ptr = new int;
    // Forgot to use delete ptr;}
// C Examplevoid func() {
    int* ptr = (int*)malloc(sizeof(int));
    // Forgot to use free(ptr);}

In the above function, memory is allocated but not released, and when the function ends, the memory is still occupied, resulting in a memory leak.

2. Exception causes memory leak

When an exception occurs in the program, if memory is allocated before the exception occurs but has not been released, and the memory release is not handled correctly during the exception processing, it will lead to memory leakage.

#include <iostream>
#include <stdexcept>

void func() {
    int* ptr = new int;
    try {
        // throw an exception        throw std::runtime_error("Something went wrong");
    } catch (const std::exception& e) {
        std::cerr << () << std::endl;
        // Not freeing ptr causes memory leak    }
}

The correct way to do this is to ensure that memory is freed during exception handling:

#include <iostream>
#include <stdexcept>

void func() {
    int* ptr = new int;
    try {
        // throw an exception        throw std::runtime_error("Something went wrong");
    } catch (const std::exception& e) {
        std::cerr << () << std::endl;
    }
    delete ptr;  // Free memory}

3. The pointer in the container is not released correctly

When a container is stored in a pointer and the container is destroyed, memory leaks can occur if the memory pointed to by the pointer is not correctly deleted.

#include <iostream>
#include <vector>

int main() {
    std::vector<int*> vec;
    for (int i = 0; i < 10; ++i) {
        int* ptr = new int(i);
        vec.push_back(ptr);
    }
    // When the container is destroyed, the memory pointed to by the stored pointer is not released.    return 0;
}

The memory pointed to by the stored pointer should be manually released before the container is destroyed:

#include <iostream>
#include <vector>

int main() {
    std::vector<int*> vec;
    for (int i = 0; i < 10; ++i) {
        int* ptr = new int(i);
        vec.push_back(ptr);
    }
    for (int* ptr : vec) {
        delete ptr;
    }
    return 0;
}

4. Memory leak caused by circular references

When using smart pointers, if a circular reference occurs, it may cause memory to be unfreed.

#include &lt;iostream&gt;
#include &lt;memory&gt;

class A;
class B;

class A {
public:
    std::shared_ptr&lt;B&gt; b_ptr;
};

class B {
public:
    std::shared_ptr&lt;A&gt; a_ptr;
};

int main() {
    std::shared_ptr&lt;A&gt; a = std::make_shared&lt;A&gt;();
    std::shared_ptr&lt;B&gt; b = std::make_shared&lt;B&gt;();
    a-&gt;b_ptr = b;
    b-&gt;a_ptr = a;
    // When the main function ends, a and b refer to each other and cannot free memory    return 0;
}

The solution is to usestd::weak_ptrBreaking circular references:

#include <iostream>
#include <memory>

class A;
class B;

class A {
public:
    std::shared_ptr<B> b_ptr;
};

class B {
public:
    std::weak_ptr<A> a_ptr;
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
    return 0;
}

5. Error using global or static variables

If a dynamically allocated pointer is stored in a global or static variable and is not released correctly, it may result in a memory leak.

#include &lt;iostream&gt;

class MyClass {
public:
    int* data;
    MyClass() {
        data = new int[100];
    }
};

MyClass globalObj;  // Global object
int main() {
    // When the program ends, it is not released, resulting in memory leak    return 0;
}

Memory can be freed in the destructor of the global object:

#include &lt;iostream&gt;

class MyClass {
public:
    int* data;
    MyClass() {
        data = new int[100];
    }
    ~MyClass() {
        delete[] data;
    }
};

MyClass globalObj;  // Global object
int main() {
    return 0;
}

6. File handle or resource not closed

Although not a direct memory leak, leaks in file handles or other system resources may indirectly affect memory usage. For example, not closing after opening a file or network connection will cause resource exhaustion, which will affect memory.

#include &lt;iostream&gt;
#include &lt;fstream&gt;

int main() {
    std::ofstream file("");
    // Forgot to use ();    return 0;
}

The correct way to do it is:

#include &lt;iostream&gt;
#include &lt;fstream&gt;

int main() {
    std::ofstream file("");
    // Operate files    ();
    return 0;
}

By avoiding the common situations above, the possibility of memory leaks in the program can be significantly reduced and the performance and stability of the program can be improved.

How to use smart pointers to avoid memory leaks?

Here are detailed instructions for using smart pointers to avoid memory leaks:

1. std::unique_ptr

  • Features
    • std::unique_ptrIt is a smart pointer with exclusive ownership, and there can only be one at the same time.std::unique_ptrOwner ownership of an object.
    • whenstd::unique_ptrWhen destroyed, the object it points to will be automatically deleted.
    • Cannot copystd::unique_ptr, but can move it.
  • Sample code
#include &lt;iostream&gt;
#include &lt;memory&gt;

class MyClass {
public:
    MyClass() {
        std::cout &lt;&lt; "MyClass constructor called" &lt;&lt; std::endl;
    }
    ~MyClass() {
        std::cout &lt;&lt; "MyClass destructor called" &lt;&lt; std::endl;
    }
    void print() {
        std::cout &lt;&lt; "Hello from MyClass" &lt;&lt; std::endl;
    }
};

int main() {
    // Create std::unique_ptr using std::make_unique    std::unique_ptr&lt;MyClass&gt; ptr = std::make_unique&lt;MyClass&gt;(); 
    ptr-&gt;print();
    // When ptr leaves the scope of the main function, it will automatically call the destructor of MyClass    return 0;
}
  • Code explanation
    • std::make_unique<MyClass>()Used to create aMyClassObject and store it instd::unique_ptrmiddle.
    • ptr->print();CallMyClassThe object'sprintMethod, prove that the object is used normally.
    • whenptrExceedmainWhen the range of the function,MyClassThe destructor will be called automatically without manual callsdelete

2. std::shared_ptr

  • Features
    • std::shared_ptrAllows multiple smart pointers to share ownership of the same object.
    • It uses a reference counting mechanism when the last onestd::shared_ptrWhen destroyed, the object will be deleted.
    • Can be copiedstd::shared_ptr, and they all point to the same object.
  • Sample code
#include &lt;iostream&gt;
#include &lt;memory&gt;

class MyClass {
public:
    MyClass() {
        std::cout &lt;&lt; "MyClass constructor called" &lt;&lt; std::endl;
    }
    ~MyClass() {
        std::cout &lt;&lt; "MyClass destructor called" &lt;&lt; std::endl;
    }
    void print() {
        std::cout &lt;&lt; "Hello from MyClass" &lt;&lt; std::endl;
    }
};

int main() {
    // Create std::shared_ptr using std::make_shared    std::shared_ptr&lt;MyClass&gt; ptr1 = std::make_shared&lt;MyClass&gt;(); 
    std::shared_ptr&lt;MyClass&gt; ptr2 = ptr1; 
    ptr1-&gt;print();
    ptr2-&gt;print();
    // When both ptr1 and ptr2 are out of scope, the MyClass destructor will be called    return 0;
}
  • Code explanation
    • std::make_shared<MyClass>()Create oneMyClassObjects and stored instd::shared_ptrmiddle.
    • std::shared_ptr<MyClass> ptr2 = ptr1;letptr2Shareptr1The ownership of the object pointed to, the reference count is increased by 1.
    • whenptr1andptr2When all out of scope, the reference count becomes 0.MyClassThe destructor will be called automatically.

3. std::weak_ptr

  • Features
    • std::weak_ptris a weak reference, it will not increasestd::shared_ptrreference count.
    • Usually used to solvestd::shared_ptrThe problem of circular reference between the two.
  • Sample code
#include &lt;iostream&gt;
#include &lt;memory&gt;

class A;
class B;

class A {
public:
    std::shared_ptr&lt;B&gt; b_ptr;
    ~A() {
        std::cout &lt;&lt; "A's destructor called" &lt;&lt; std::endl;
    }
};

class B {
public:
    std::weak_ptr&lt;A&gt; a_ptr;
    ~B() {
        std::cout &lt;&lt; "B's destructor called" &lt;&lt; std::endl;
    }
};

int main() {
    std::shared_ptr&lt;A&gt; a = std::make_shared&lt;A&gt;();
    std::shared_ptr&lt;B&gt; b = std::make_shared&lt;B&gt;();
    a-&gt;b_ptr = b;
    b-&gt;a_ptr = a;
    // When the main function ends, there will be no memory leak due to loop references    return 0;
}
  • Code explanation
    • std::make_shared<A>()andstd::make_shared<B>()Created separatelyAandBObjects and stored instd::shared_ptrmiddle.
    • a->b_ptr = b;andb->a_ptr = a;Will cause a circular reference ifa_ptrToostd::shared_ptr, it will cause memory leaks.
    • But usestd::weak_ptrThe reference count will not be increased whenmainWhen the function ends,aandbThe destructors of  will be called correctly because they do not maintain each other's life cycle.

summary

  • usestd::unique_ptrIt can ensure the automatic release of exclusive resources and is suitable for most cases where shared resources are not required.
  • std::shared_ptrApplicable to situations where shared resources are required, but be careful to avoid circular references, otherwise it may lead to memory leaks.
  • std::weak_ptrCan be used to solvestd::shared_ptrThe circular reference problem caused, it does not affect the life cycle of the object, but it can be checked whether the object still exists.

By using these smart pointers, you can avoid the possible problems such as forgetting to free memory, exceptions that may cause memory failure to free memory when manually managing memory, thereby avoiding memory leakage.

at last

The above is the detailed content of C++ memory leak detection and solution. For more information about C++ memory leak detection and resolution, please pay attention to my other related articles!