C++ memory leaks and detection tools

  • 2020-04-02 01:47:05
  • OfStack

First we need to know if there is a memory leak in the program, and then we need to figure out which line of code is leaking so we can fix it.

Of course, the easiest way is to use professional detection tools, such as BoundsCheck, is very powerful, believe that C++ development people can not do without it. In addition, it does not use any tools, but to achieve their own memory leak monitoring, divided into the following two cases:

I. detect memory leak in MFC

If be the program that USES MFC, very simple. Memory leak detection is available by default.

We used VS2005 to generate an MFC dialog program, and found that it can automatically detect memory leaks.


#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEW is a macro defined in the afx.h file that helps us locate memory leaks.

If memory is not deleted after the CPP file containing the above code is allocated, the following information will be displayed in the Output window of VisualStudio when the program is stopped:

Detected the memory leaks!
Dumping objects - >
D :\code\mfctest\mfctest.cpp(80) : {157} normal block at 0x003AF170, 4 bytes long.
  Data: < > 00 00 00 00
The Object dump complete.

Double-click the bold line in the Output window, and the IDE will open the file, locate the line, and it's easy to see where the memory leak is.

two Detect C++ - only program memory leaks

I tried the Win32 Console Application and Win32 Project projects created with VisualStudio, but they both failed to detect memory leaks.

The next step is to establish the memory leak detection mechanism of the program.

First, we need to know that the Debug version of the C runtime provides a lot of detection capabilities to make our debuggers easier. There is a section in MSDN called Debug Routines for this, so I suggest you look at it first.

We're going to use a couple of functions that are important. The most important of these is _CrtDumpMemoryLeaks(); See for yourself the help in MSDN. To use this function, you need to include the header file crtdbg.h

This function is only useful in the Debug version, when running the program under the debugger, _CrtDumpMemoryLeaks will display memory leaks in the "Output" window.

Detect memory leak version 1:


#include "stdafx.h"
#include <crtdbg.h>
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    _CrtDumpMemoryLeaks();
    return 0;
}

After running, the following information is displayed in the Output window:

Detected the memory leaks!
Dumping objects - >
{112} normal block at 0x003AA770, 4 bytes long.
  Data: <       > 00 00 00 00
The Object dump complete.

But this just tells us that there is a memory leak in the program.

Look at our leak detection version 2:


#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    _CrtDumpMemoryLeaks();
    return 0;
}

This program defines several macros, by which the new under Debug version is replaced, and the new new records the file name and line of code when calling new. After running, you can see the following results:

Detected the memory leaks!
Dumping objects - >
D :\code\consoletest\consoletest.cpp(21) : {112} client block at 0x003A38B0, subtype 0, 4 bytes long.
  Data: <       > 00 00 00 00
The Object dump complete.

Ha ha, already and the effect of MFC program is same, but wait a moment. Take a look at the following code:


int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    _CrtDumpMemoryLeaks();
    delete p;
    return 0;
}

After running, we can find that we have deleted the pointer, but it still reports a memory leak. So imagine that every time new is called, the call is recorded internally in the program, similar to having an array record and deleting it from the array if it is deleted, and _CrtDumpMemoryLeaks() prints out the current state of the array.

So in addition to dumping out memory information when necessary, the most important thing is to Dump _CrtDumpMemoryLeaks() when the program exits;

If the program has more than one exit, we need to call the function in multiple places.

Further, what if the program deletes Pointers from the destructor of the class? Such as:


#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
    Test()      {   _p = new int();     }
    ~Test()     {   delete _p;          }
    int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
    int* p = new int();
    delete p;
    Test t;
    _CrtDumpMemoryLeaks();
    return 0;
}

You can see that the destructor is called when the program exits, so there is no memory leak, but this is still reported.

How to improve, look at detecting memory leaks version 3:


#include "stdafx.h"
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
class Test
{
public:
    Test()      {   _p = new int();     }
    ~Test()     {   delete _p;          }
    int* _p;
};
int _tmain(int argc, _TCHAR* argv[])
{
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
    int* p = new int();
    delete p;
    Test t;
    return 0;
}

  _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); This statement automatically calls _CrtDumpMemoryLeaks when the program exits. Both _CRTDBG_ALLOC_MEM_DF and _CRTDBG_LEAK_CHECK_DF must be set.

So, this version has been achieved the same effect as the MFC, but I think this is not enough light, because we are only in the Output window Output information, remind of developers is not obvious, often missing, and a lot of people even if find memory leaks, but bad repair, not seriously affect the external program performance, won't fix. How do you get developers to actively fix memory leaks? Remember and write program, I have a request, the function parameters can't be empty, but other people always pass null values, no way out, so we have to verify the start of the function function parameters, to assert his live, this program is running is always kept popup assert, the debugger that bother pressure, finally got bored of other programmers, give change well, the problem is the right input parameters. So I think we should let the programmer take the initiative to do a thing, the first thing is to let him feel that doing this thing is to reduce their burden, make their work easy. Ha ha, that we also like this, when the program exit, detect memory leak let the program prompt.

Look at detecting memory leaks version four:


#include "stdafx.h"
#include <assert.h>
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
void Exit()
{
    int i = _CrtDumpMemoryLeaks();
    assert( i == 0);
}
int _tmain(int argc, _TCHAR* argv[])
{
    atexit(Exit);
    int* p = new int();
    return 0;
}

This version checks for a memory leak when the program exits, and a dialog box pops up if there is one.

Atexit (Exit); The Exit() function is set to execute when the program exits. In the Exit() function, if there is a memory leak, _CrtDumpMemoryLeaks() returns a non-zero value and is asserted.

This version is ready to use. However, we can make some improvements, because to accurately detect all memory leaks in the code, we need to put #define in the code... Copy to all files that use new. It's impossible to copy so much code for every file, so we can pull it out and put it in a file like KDetectMemoryLeak.


#pragma once
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

Then include KDetectMemoryLeak. H in the project's general file, such as stdafx.h for projects built with VS. Or a common.h file that I built myself, which contains some generic code that basically all files use.


Related articles: