Memory leaks in C++ generally refer to memory leaks in the heap. The heap memory is manually allocated by malloc/realloc/new, the program will not be automatically reclaimed, you need to call free or delete to manually release, otherwise it will cause memory leak. In fact, memory leaks should also include leaks of system data, such as socket connections, etc., which should also be released after use.

Causes of memory leakage:

To sum up, there are probably several reasons for memory leaks:

1, coding error: malloc, realloc, new applications exist on the heap, need to manually display the release, call free or delete. Apply and release must be paired malloc/realloc for free and new for DELETE. The former does not run the constructor/destructor, the latter does. For C++ built-in data types there may be no difference, but for classes you construct, you may free system resources or memory in the destructor, so use it accordingly.

2. “unoccupied” memory: After the memory is applied, the pointer points to the starting address of the memory. If the pointer is lost or modified, the memory will be lost and not released.

3. Abnormal branches lead to unreleased resources: There is no problem with the normal execution of the program, but if exceptions are encountered, the normal execution sequence or branches will be interrupted and cannot be executed. So in exception handling code, make sure that system resources are released.

4. Implicit memory leak: memory is continuously allocated during the execution of the program, but is not released until the end of the program. Some servers request a large amount of memory as a cache, or a large amount of Socket resources as a connection pool, and these resources are used until the program exits. Servers typically run for several months, and memory may run out if not released.

5. Destructors for classes are non-virtual: Destructors are virtual and use polymorphism to call destructors that point to objects rather than base classes.


Memory leak detection

The key to a memory leak is to log the memory allocated and the memory freed operations to see if they match. Track the declaration cycle of each piece of memory. For example, after applying for a piece of memory, add the pointer to it to the List. When releasing, delete the corresponding pointer from the List, and check the List at the end of the program to know whether there is a memory leak. The Visual Studio debugger for Windows and the C runtime (CRT) use this principle to detect memory leaks.

When used in VS, it must be added

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>

Crtdbg.h maps the malloc and free functions to their Debug versions _malloc_DBg and _free_DBg, which track memory allocation and release (valid in Debug)

_CrtDumpMemoryLeaks();

The function will display the current memory leak, that is, the memory leak when the program runs to this line of code. All undestroyed objects will report a memory leak, so keep this function as late as possible.

Such as:

[cpp] view plain copy

  1. #define _CRTDBG_MAP_ALLOC  
  2. #include <crtdbg.h>  
  3. #include <iostream>  
  4. using namespace std;  
  5. int main(int argc,char** argv)  
  6. {  
  7.     char *str1 = NULL;  
  8.     char *str2 = NULL;  
  9.     str1=new char[100];  
  10.     str2=new char[50];  
  11.   
  12.     delete str1;  
  13.     _CrtDumpMemoryLeaks();  
  14.     return 0;  
  15. }  

In the above code, two pieces of memory are allocated, but only one is freed. Run debugging, and output window:

Dumping objects ->

{136} normal block at 0x00084D70, 50 bytes long.

Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 

Object dump complete.

You can see that memory leaks are detected. However, the location of the leaked memory request was not detected, and the macro definition #define _CRTDBG_MAP_ALLOC has been added. Malloc does not override the new operator, so you have to define the overloaded new operator to detect the location of the memory leak. Modified as follows:

[cpp] view plain copy

  1. #define _CRTDBG_MAP_ALLOC  
  2. #include <crtdbg.h>  
  3. #ifdef _DEBUG // reload new
  4. #define new  new(_NORMAL_BLOCK, __FILE__, __LINE__)    
  5. #endif  
  6. #include <iostream>  
  7. using namespace std;  
  8. int main(int argc,char** argv)  
  9. {  
  10.     char *str1 = NULL;  
  11.     char *str2 = NULL;  
  12.     str1=(char*)malloc(100);  
  13.     str2=new char[50];  
  14.   
  15.     _CrtDumpMemoryLeaks();  
  16.     return 0;  
  17. }  

Running results:

Detected memory leaks! Object -> e:\c++\test\ memory overflow 2\main.cpp(13) : {62} normal block at 0x001714F8, 50 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD e:\c++\test\ memory leak detection 2\main.cpp(12) : {61} normal block at 0x00171458, 100 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete.

You can see

The number in parentheses main.cpp() is where the memory leak started. {62} normal block at 0x001714F8, 50 bytes long. What does it stand for?

The numbers inside the braces {} indicate the number of memory requests; 0x001714F8 indicates the start address of memory leakage. CD CD indicates the content of memory leakage.

Why is this the 62nd memory request? Because memory was requested during the initialization operation. With this information, you can set breakpoints. Call long _CrtSetBreakAlloc(long nAllocID) to make the nAllocID request interrupt, and get more information at the interrupt than at the termination of the program. You can debug, view variable states, debug function calls, and resolve memory leaks.

There are three types of blocks: Normal (common), client (dedicated to MFC), CRT (run time), free (released blocks), and IGore (ignored blocks).

In the above program, _CrtDumpMemoryLeaks() is called to detect memory leaks, which is cumbersome and must be called in multiple places if the program is likely to end up in more than one place, Starting position in program can call _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF), so that whenever the program is terminated, will call before termination _CrtDumpMemoryLeaks ().

 

In addition, you can set checkpoints at a certain time to take a snapshot of the state of memory at that time. Compare the memory state at different times.

[cpp] view plain copy

  1. #define _CRTDBG_MAP_ALLOC  
  2. #include <crtdbg.h>  
  3. #ifdef _DEBUG // reload new
  4. #define new  new(_NORMAL_BLOCK, __FILE__, __LINE__)    
  5. #endif  
  6. #include <iostream>  
  7. using namespace std;  
  8. int main(int argc,char** argv)  
  9. {  
  10.     _CrtMemState s1, s2, s3;  
  11.     char *str1 = NULL;  
  12.     char *str2 = NULL;  
  13.     str1=(char*)malloc(100);  
  14. _CrtMemCheckpoint( &s1 ); // Record a memory snapshot
  15. _CrtMemDumpStatistics( &s1 ); / / output
  16.     str2=new char[50];  
  17.     _CrtMemCheckpoint( &s2 );  
  18.     _CrtMemDumpStatistics( &s2 );  
  19.   
  20. If (_CrtMemDifference(&s3, &s1, &s2)
  21. _CrtMemDumpStatistics( &s3 ); // dump the difference result
  22.   
  23.     return 0;  
  24. }  

The output is:

0 bytes in 0 Free Blocks. 100 bytes in 1 Normal Blocks. 8434 bytes in 54 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 8963 bytes. Total allocations: 14003 bytes. 0 bytes in 0 Free Blocks. 150 bytes in 2 Normal Blocks. 8434 bytes in 54 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 8963 bytes. Total allocations: 14053 bytes. 0 bytes in 0 Free Blocks. 50 bytes in 1 Normal Blocks. 0 bytes in 0 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 0 bytes. Total allocations: 50 bytes.

 

You can also use this method to detect memory leaks in more complex ways, such as setting up checkpoints to check for memory leaks between checkpoints.