VS環境中進行內存泄漏的檢測

根據MSDN中的介紹,親測整理。api

本篇比較長,如不肯花費太多時間,可只看第一段和第四段,甚至只看第四段。多線程

內存泄漏,即未能正確釋放之前分配的內存,是 C/C++ 應用程序中最難以捉摸也最難以檢測到的 Bug 之一。藉助 Visual Studio 調試器和 C 運行時 (CRT) 庫,能夠檢測和識別內存泄漏。檢測內存泄漏的主要工具是調試器和 C 運行庫 (CRT) 調試堆函數。函數

簡單的使用工具

要調用CRT調試堆函數,需包含頭文件<crtdbg.h>。spa

在程序的退出點以前調用函數

_CrtDumpMemoryLeaks.net

();可簡單的顯示內存泄漏報告。
示例:
#include "stdlib.h"
#include <crtdbg.h>
int main()
{
    char *p =new char[30];
    char *p1=(char *)malloc(sizeof(char)*10);
    _CrtDumpMemoryLeaks();
    return 0;
}

輸出結果:插件

clip_image002

當程序有多個退出點時,在每個退出點都調用_CrtDumpMemoryLeaks()顯然不是一個好主意,在程序的開頭部分調用函數_CrtSetDbgFlag會致使在每一個退出點自動調用 _CrtDumpMemoryLeaks。如此調用_CrtSetDbgFlag必須設置兩個位域線程

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );3d

解釋內存泄漏報告調試

{64}—— 表示是在第64次分配的內存,最終沒有被釋放掉,從而致使泄漏。CRT 報告包含運行 過程當中的全部內存塊分配狀況。其中包括 CRT 庫和其餘庫(如 MFC)的分配狀況。 因此不要疑惑這裏爲何不是2.

Normal block—— 表示被泄露的內存塊爲由程序分配的普通內存。另外,這個值還有多是

Client Blocks ,「客戶端塊」是由 MFC 程序用於須要析構函數的對象的特殊 類型內存塊。 MFC new 運算符根據正在建立的對象的須要建立普通塊或客 戶端塊。

0x004C1DA0—— 表示發生泄漏的內存位置

10 bytes long.—— 遭泄露的內存塊的大小

Data: < > CD CD CD CD CD CD CD CD CD CD —— 遭泄露的塊中的數據,通常只顯示前16個字節

上述內存泄漏報告確實給出了很多信息,但很重要的一點卻沒有給出,那就是發生泄漏的代碼位置,畢竟咱們須要據此來改善代碼。

獲取更詳細的內存泄漏報告

對於堆函數(例如 malloc、 free、 callocrealloc、 new 和 delete)都有其對應的調試版本,若是在檢測內存泄漏的過程當中使用這些堆函數的調試版本,則能夠輸出更詳細的內存泄漏報告。這並不難。

經過定義宏_CRTDBG_MAP_ALLOC可使malloc,free等函數映射到它們的調試版本。

對於C++中的new和delete操做符,稍麻煩點,須要從新定義 new 才能在內存泄漏報告中看到文件和行號。

#ifdef _DEBUG

#ifndef DBG_NEW

#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )

#define new DBG_NEW

#endif

#endif // _DEBUG

示例:

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

#ifdef _DEBUG
    #ifndef DBG_NEW
        #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
        #define new DBG_NEW
    #endif
#endif  // _DEBUG
int main()
{
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF |         \            _CRTDBG_LEAK_CHECK_DF );
    char *p =new char[30];
    char *p1=(char *)malloc(sizeof(char)*10);
//    _CrtDumpMemoryLeaks();
    return 0;
}

輸出結果:

clip_image005

至此,已經足夠知足咱們基本的需求了。

須要注意的一點是,宏_CRTDBG_MAP_ALLOC的定義必須放在include< crtdbg.h >以前,這表示在編譯crtdbg.h時,會根據這個宏來選擇編譯上述堆函數的Debug版本。


鎖定內存泄漏位置的另外一個辦法是在內存分配編號上設置斷點。

1. 在應用程序的起點附近設置斷點,而後啓動應用程序。

2. 當應用程序在斷點處中斷時,會出現 「監視」窗口。

3. 在 「監視」窗口中,在 「名稱」列中鍵入 _crtBreakAlloc。

4. 若是要使用 CRT 庫的多線程 DLL 版本(/MD 選項),請加入上下文運算符: {,,msvcr100d.dll}_crtBreakAlloc(針對vs2010是msvcr100d.dll,其餘版本的環境找對應的dll)

5. 在 「值」列中,將顯示的值替換爲要在其位置中斷的內存分配的分配編號。

clip_image006

clip_image008

與其類似的另外一個作法是在代碼中設置內存分配斷點。具體作法時在代碼的起始位置附近,調用以下函數(實際上是個宏):

_CrtSetBreakAlloc(61);

參數61表示內存分配的分配編號,可由上述簡單的使用得到。

這一下,咱們得到了更多詳細的報告——調用堆棧,能夠更準確的鎖定發生內存泄漏的位置。但這種方法的不足之處是,對於在多線程代碼中,因爲每次調試時的內存分配編號都會變化,就無法使用這種辦法了。


定位內存泄漏的另外一種技術涉及在關鍵點對應用程序的內存狀態拍快照。

若要爲應用程序中給定點的內存狀態拍快照,請建立 _CrtMemState 結構,將它傳遞給 _CrtMemCheckpoint 函數。

該函數用當前內存狀態的快照填充此結構:

_CrtMemState s1;

_CrtMemCheckpoint( &s1 );

若要輸出 _CrtMemState 結構的內容,請將該結構傳遞給 _ CrtMemDumpStatistics 函數:

_CrtMemDumpStatistics( &s1 );

若要肯定在某個代碼部分中是否發生了內存泄漏,能夠對這部分以前和以後的內存狀態拍快照,而後使用 _CrtMemDifference 比較兩個狀態:

_CrtMemCheckpoint( &s1 );

// memory allocations take place here

_CrtMemCheckpoint( &s2 );

if ( _CrtMemDifference( &s3, &s1, &s2) )

_CrtMemDumpStatistics( &s3 );

示例代碼

#include "stdlib.h"
#include <crtdbg.h>
#include <Windows.h>
int main()
{
    _CrtMemState s1;
    _CrtMemState s4;
    _CrtMemCheckpoint( &s1 );
    OutputDebugString("第一次內存快照\n");
    _CrtMemDumpStatistics(&s1);
    char *p =new char[30];
    _CrtMemState s2;
    _CrtMemCheckpoint( &s2 );
    OutputDebugString("第二次內存快照\n");
    _CrtMemDumpStatistics(&s2);
    if ( _CrtMemDifference( &s4, &s1, &s2) )
    {    
        OutputDebugString("前兩次內存快照的差別\n");
        _CrtMemDumpStatistics( &s4 );
    }
    char *p1=(char *)malloc(sizeof(char)*10);
    _CrtMemState s3;
    _CrtMemCheckpoint( &s3 );
    OutputDebugString("第三次內存快照\n");
    _CrtMemDumpStatistics(&s3);
    if ( _CrtMemDifference( &s4, &s1, &s3) )
    {    
        OutputDebugString("首尾內存快照的差別\n");
        _CrtMemDumpStatistics( &s4 );
    }
    return 0;
}

clip_image011
普通塊是由程序分配的普通內存。

客戶端塊是由 MFC 程序用於須要析構函數的對象的特殊類型內存塊。

MFC new 運算符根據正在建立的對象的須要建立普通塊或客戶端塊。

「CRT 是由 CRT 庫爲本身使用而分配的內存塊。

CRT 庫可處理這些塊的釋放。

所以,不大可能在內存泄漏報告中看到這些塊,除非出現嚴重錯誤(例如 CRT 庫損壞)。

內存泄漏報告中絕對不會出現另外兩個內存塊類型。

可用塊是已釋放的內存。

也就是說,根據定義,這種塊不會泄漏。

忽略塊是已明確標記、不出如今內存泄漏報告中的塊。

尋找內存泄漏的一個方法是,首先在應用程序的開頭和結尾部分放置 _CrtMemCheckpoint 調用,而後使用 _CrtMemDifference 比較兩個結果。

若是 _CrtMemDifference 顯示有內存泄漏,能夠添加更多 _CrtMemCheckpoint 來進一步找到泄漏源。

這個方法看起來真夠笨拙的,不過它或許有別的好處吧。誰知道呢?


使用第三方工具

或許有人會嫌上述方法都太麻煩了,那不妨試試第三方工具,目前用於檢測內存泄漏的第三方工具可謂多種多樣,這裏只推薦一款用與vs環境的第三方插件:Visual Leak Detector ,推薦理由:開源,免費。

下載地址:http://vld.codeplex.com/releases

目前已更新版本至v2.4rc2

使用方法及效果以下:

clip_image013

其調用堆棧的分析在控制檯應用上並不十分準確,忽略了咱們最關注的源代碼文件中調用位置。不過其在MFC中的表現還不賴。

以上源自 http://blog.csdn.net/yapingxin/article/details/6751940,話說這個blog裏確實有不少好東西。

相關文章
相關標籤/搜索