Memory leak and its detection

1. Definition of memory leak 

   Generally speaking, memory leaks refer to heap memory leaks. Heap memory refers to the memory allocated by the program from the heap, and the size is arbitrary (the size of the memory block can be determined during the runtime of the program), and the freed memory must be displayed after use. Applications generally use malloc, realloc, new and other functions to allocate a block of memory from the heap. After use, the program must be responsible for calling free or delete to release the memory block. Otherwise, the memory cannot be used again. Say this memory leak.

Example one:

The following small program demonstrates a situation where heap memory leaks:

void MyFunction(int nSize)
{
 char* p= new char[nSize];
 if( !GetStringFrom( p, nSize ) ){
  MessageBox(“Error”);
  return;
 }
 …//using the string pointed by p;
 delete p;
}

  When the function GetStringFrom() returns zero, the memory pointed to by the pointer p will not be freed. This is a common memory leak situation. The program allocates memory at the entry and releases the memory at the exit, but the c function can exit anywhere, so once there is a certain exit that does not release the memory that should be released, a memory leak will occur. Broadly speaking, memory leaks not only include heap memory leaks, but also system resource leaks (resource leaks), such as core state HANDLE, GDI Object, SOCKET, Interface, etc. Fundamentally, these objects allocated by the operating system are also Consume memory, and if these objects leak, it will eventually lead to memory leaks. Moreover, some objects consume kernel-mode memory, and when these objects are seriously leaked, the entire operating system will become unstable. So in contrast, the leak of system resources is more serious than the leak of heap memory.

Example two:

void CMyView::OnPaint( CDC* pDC )
{
 CBitmap bmp;
 CBitmap* pOldBmp;
 bmp.LoadBitmap(IDB_MYBMP);
 pOldBmp = pDC->SelectObject( &bmp );
 …
 if( Something() ){
  return;
 }
 pDC->SelectObject( pOldBmp );
 return;
}

    When the function Something() returns non-zero, the program does not select pOldBmp back into pDC before exiting, which will cause the HBITMAP object pointed to by pOldBmp to leak. If this program runs for a long time, it may cause the whole system to hang up. This kind of problem is easier to expose under Win9x, because the GDI heap of Win9x is much smaller than that of Win2k or NT.

Example three:

char* g_lpszFileName = NULL;

void SetFileName( const char* lpcszFileName )
{
 if( g_lpszFileName ){
  free( g_lpszFileName );
 }
 g_lpszFileName = strdup( lpcszFileName );
}

    If the program ends without freeing the string pointed to by g_lpszFileName, even if SetFileName() is called multiple times, there will always be a block of memory, and only one block of memory will leak.

Example four:

class Connection
{
 public:
  Connection( SOCKET s);
  ~Connection();
  …
 private:
  SOCKET _socket;
  …
};

class ConnectionManager
{
 public:
  ConnectionManager(){}
  ~ConnectionManager(){
   list::iterator it;
   for( it = _connlist.begin(); it != _connlist.end(); ++it ){
    delete (*it);
   }
   _connlist.clear();
  }
  void OnClientConnected( SOCKET s ){
   Connection* p = new Connection(s);
   _connlist.push_back(p);
  }
  void OnClientDisconnected( Connection* pconn ){
   _connlist.remove( pconn );
   delete pconn;
  }
 private:
  list _connlist;
};

Assuming that after the Client is disconnected from the Server, the Server does not call the OnClientDisconnected() function, then the Connection object representing that connection will not be deleted in time (when the Server program exits, all Connection objects will be analyzed in the ConnectionManager is deleted in the constructor). Implicit memory leaks occur when there are constant connections being established and disconnected. The program keeps allocating memory while it is running, but does not release the memory until it finishes. Strictly speaking, there is no memory leak here, because the final program releases all the allocated memory. But for a server program that needs to run for days, weeks, or even months, not releasing the memory in time may also lead to eventually exhausting all the memory of the system. Therefore, we call this type of memory leak an implicit memory leak.

2. Memory detection

2.1 Step 1

Include the following statements in the program (#include statements must be in the order shown above. If the order is changed, the functions used may not work correctly.)

#define
 _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>

2.2 Step 2

After adding the above statement, memory leak information can be dumped by including _CrtDumpMemoryLeaks() in the program as follows:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
using namespace std;
 
void GetMemory(char *p, int num)
{
    p = (char*)malloc(sizeof(char) * num);
}
 
int main(int argc,char** argv)
{
    char *str = NULL;
    GetMemory(str, 100);
    cout<<"Memory leak test!"<<endl;
    _CrtDumpMemoryLeaks();
    return 0;
}

If the program always exits in the same place, calling _CrtDumpMemoryLeaks() will be very easy. If the program exits from multiple locations, instead of placing a call to _CrtDumpMemoryLeaks() at every possible exit location, you can include the following call at the beginning of the program:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

This statement automatically calls _CrtDumpMemoryLeaks() when the program exits, and the two bit fields _CRTDBG_ALLOC_MEM_DF and _CRTDBG_LEAK_CHECK_DF must be set at the same time.

3. Locate specific memory leaks

Through the above method, we can almost locate where the memory allocation functions malloc and new are called, as in the GetMemory function in the above example, that is, line 10! But I can't locate where the memory leak is caused by calling GetMemory(), and in a large project there may be many calls to GetMemory. How to locate memory leaks caused by calls to GetMemory. Another technique for locating memory leaks involves taking snapshots of the application's memory state at key points. The CRT library provides a struct type _CrtMemState that you can use to store snapshots of the memory state:
_CrtMemState s1, s2, s3;
To take a snapshot of the memory state at a given point, pass the _CrtMemState structure to the _CrtMemCheckpoint function. The function fills this structure with a snapshot of the current memory state:
_CrtMemCheckpoint( &s1 );
The contents of the structure can be dumped at any point by passing the _CrtMemState structure to the _CrtMemDumpStatistics function:
_CrtMemDumpStatistics( &s1 );
To determine if a memory leak has occurred in a section of your code, you can take a snapshot of the memory state before and after the section, then use _CrtMemDifference to compare the two states. As the name suggests, _CrtMemDifference compares two memory states (s1 and s2), Generate a result (s3) of the difference between these two states. Placing _CrtMemCheckpoint calls at the beginning and end of the program and comparing the results using _CrtMemDifference is another way to check for memory leaks. If a leak is detected, the _CrtMemCheckpoint call can be used to partition the program and locate the leak through binary search techniques.
_CrtMemCheckpoint( &s1 );
// memory allocations take place here
_CrtMemCheckpoint( &s2 );
 
if ( _CrtMemDifference( &s3, &s1, &s2) )
   _CrtMemDumpStatistics( &s3 );
We now improve the original example:
    Placing _CrtMemCheckpoint() calls at the beginning and end of the program and comparing the results with _CrtMemDifference() is another way to check for memory leaks. If a leak is detected, the _CrtMemCheckpoint() call can be used to partition the program and locate the leak through binary search techniques.
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
using namespace std;
_CrtMemState s1, s2, s3;
 
void GetMemory(char *p, int num)
{
    p = (char*)malloc(sizeof(char) * num);
}
 
int main(int argc,char** argv)
{
    _CrtMemCheckpoint( &s1 );
    char *str = NULL;
    GetMemory(str, 100);
    _CrtMemCheckpoint( &s2 );
    if ( _CrtMemDifference( &s3, &s1, &s2) )
        _CrtMemDumpStatistics( &s3 );
    cout<<"Memory leak test!"<<endl;
    _CrtDumpMemoryLeaks();
    return 0;
}

4. How to avoid memory leaks

    1. If the constructor creates an object and saves it with a member pointer variable, it must be deleted in the destructor, and there is no way to transfer the ownership of the object for some convenience.
    2. When you can use shared_ptr, try to use shared_ptr. Shared_ptr As long as you don't have a circular reference, then this thing can be passed to each other safely, you can add or delete it in whatever container you want, and put it where you want, no longer need to consider the life cycle of the object.
     3. Do not use memset (or memcpy) on objects with constructors and destructors. If an object needs memset, then memset itself in the object's constructor. If you need to memset an array of objects, also memset itself in the object's constructor. If you need to memset a complex object without a constructor, add a constructor for him, unless that's something provided by someone else's API.
    4. If an object inherits other things, or some members are marked virtual, never memset. Objects are independent, that is to say, the evolution of the internal structure of the parent class does not need to be responsible for the child class. One day, a string member was added to the parent class, and the subclass was memset, and I wanted to cry without tears.
    5. If you need to define a constructor for an object, then even the copy constructor, operator= overloading and destructor are all written. If you don't want to write copy constructors and operator=, then write an empty implementation in private to ensure that any code that tries to call these functions will compile errors.
     6. If you really like the C language, then please change to a compiler that only supports C but not C++, so as to completely prevent your C from breaking due to misuse of C++.








Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325995042&siteId=291194637