1. 程式人生 > >C++中記憶體洩漏的檢查與定位

C++中記憶體洩漏的檢查與定位

本文僅僅是一些簡短講述一下,關於C++在Windows平臺下記憶體洩漏記憶體洩漏的檢查與定位。請參閱《最快速度找到記憶體洩漏》

在Windows平臺下,可以藉助標頭檔案<crtdbg.h>中定義的一以下幾個函式來完成。

_CrtDumpMemoryLeaks();
// 置於main函式最後,列印記憶體洩露報告

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
// 置於main函式開頭(噹噹前程式沒有統一退出點時)

long _CrtSetBreakAlloc(long lBreakAlloc);
//用於在給定記憶體分配操作時停止當前程式


以下是_CrtDumpMemoryLeaks()的詳細程式碼。(僅在Debug版本下有效)

#include <crtdbg.h>


void main()  
{    
    int* pLeak = new int[10];

    pLeak = nullptr;

    _CrtDumpMemoryLeaks(); 
} 


這樣在當前程式執行結束後,在Vistual Studio的Output視窗中會輸出如下資訊:

Detected memory leaks! 
Dumping objects -> 
c:/work/test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long. 
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD  
Object dump complete.

以上資訊似乎僅僅告訴了我們記憶體洩漏發生了,但依舊難以定為具體的記憶體洩漏的位置。

以下是改進後的版本:

#define _CRTDBG_MAP_ALLOC
// 用於將malloc和free函式重定向至DEBUG版本,使之能輸出對應的原始檔及行號。

#include <crtdbg.h>

#ifdef _DEBUG   
#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)   
#endif
// 重定向new關鍵字,否則對於所有的new操作,依舊無法正確輸出對應的原始檔及行號。

void main()  
{    
    int* pLeak = new int[10];

    pLeak = nullptr;

    _CrtDumpMemoryLeaks(); 
}


但有些時候,這依舊很難幫助我們找到具體的記憶體洩漏點。

我們注意到,在以上記憶體洩漏報告中有一個奇怪的資訊({52}),其表示是第52次記憶體分配時,所分配的記憶體未被回收。

方法long _CrtSetBreakAlloc(long lBreakAlloc)可以使程式停止在指定的記憶體分配操作處(即:第52次記憶體分配)。

#include <crtdbg.h>


void main()  
{
    _CrtSetBreakAlloc(52);

    int* pLeak = new int[10];

    pLeak = nullptr;

    _CrtDumpMemoryLeaks(); 
}


再次在VS中執行上述程式碼,程式會在指定處停止並進入除錯狀態。通過檢視函式呼叫堆疊,便可以精確定位出錯的程式碼行,及當時的程式執行狀態。

在這裡,還有一點要注意的是C++的全域性變數,一些全域性變數的初始化會在進入main方法前就發生了。這種情況下,上述方法依舊無效。詳見下例:

struct MyCls
{
    string SubExprStr;
};


MyCls g_MyCls = {
    string("Hello world!")
};

上述程式碼中,g_MyCls物件例項的初始化在進入main方法前就完成了。在這種情況下,可以通過設定條件斷點來完成。具體如下:

// file 'dbgheap.c'

extern "C" static void * __cdecl _heap_alloc_dbg_impl(
        size_t nSize,
        int nBlockUse,
        const char * szFileName,
        int nLine,
        int * errno_tmp
        )
{
        // ...

        /* lock the heap
         */
        _mlock(_HEAP_LOCK);
        __try {

            // ...

            /* break into debugger at specific memory allocation */
            if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
                _CrtDbgBreak();


在以下行處設定條件斷點,在變數lRequest等於給定記憶體分配次數時停止執行。

if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)


上述方法在本人的機器(Windows 7  VS2010)上測試有效,暫時未在其它情況下驗證過。