c/c++內存泄漏檢測

這幾天突然想起了解一下c/c++內存泄漏檢測方面的東西,在網上的這方面的介紹也有很多,現在我在這個把我自己感覺有用的在這裏總結一下

內存泄漏簡介及後果

wikipedia中這樣定義內存泄漏:在計算機科學中,內存泄漏指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。

最難捉摸也最難檢測到的錯誤之一是內存泄漏,即未能正確釋放以前分配的內存的 bug。 只發生一次的小的內存泄漏可能不會被注意,但泄漏大量內存的程序或泄漏日益增多的程序可能會表現出各種徵兆:從性能不良(並且逐漸降低)到內存完全用盡。 更糟的是,泄漏的程序可能會用掉太多內存,以致另一個程序失敗,而使用戶無從查找問題的真正根源。 此外,即使無害的內存泄漏也可能是其他問題的徵兆。

內存泄漏會因爲減少可用內存的數量從而降低計算機的性能。最終,在最糟糕的情況下,過多的可用內存被分配掉導致全部或部分設備停止正常工作,或者應用程序崩潰。內存泄漏可能不嚴重,甚至能夠被常規的手段檢測出來。在現代操作系統中,一個應用程序使用的常規內存在程序終止時被釋放。這表示一個短暫運行的應用程序中的內存泄漏不會導致嚴重後果。

在Windows 下的檢測:

1. 使用crtdbg.h

Windows平臺下面Visual Studio 調試器和 C 運行時 (CRT) 庫爲我們提供了檢測和識別內存泄漏的有效方法,原理大致如下:內存分配要通過CRT在運行時實現,只要在分配內存和釋放內存時分別做好記錄,程序結束時對比分配內存和釋放內存的記錄就可以確定是不是有內存泄漏。

#define _CRTDBG_MAP_ALLOC                //輸出內存泄漏的行號等信息, 這個必須放在crtdbg.h之前
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <crtdbg.h>
void GetMemory(char *p, int num)
{
    p = (char*)malloc(sizeof(char)* num);
}
int main()
{
    char *str = NULL;
    GetMemory(str, 100);

    cout << "Memory leak test!" << endl;
    _CrtDumpMemoryLeaks();

    return 0;
}

輸出結果:

Detected memory leaks!
Dumping objects ->
{152} normal block at 0x00FD4098, 4 bytes long.
Data: <    > CD CD CD CD
  e : \my projects\cpp\hellovs2013\hellovs2013\helloworld.cpp(20) : {151} normal block at 0x00FDA5A0, 100 bytes long.
  Data : <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
         Object dump complete.
         程序“[5472] HelloVs2013.exe”已退出,返回值爲 0 (0x0)。


crtdbg.h有個不好的就是雖然也能檢測到new 操作法造成的泄漏,但是不能定位泄漏的位置。

定位具體的內存泄漏地方

1 _CrtMemState s1, s2, s3;

若要在給定點對內存狀態拍快照,請向 _CrtMemCheckpoint 函數傳遞 _CrtMemState 結構。 該函數用當前內存狀態的快照填充此結構:

1 _CrtMemCheckpoint( &s1 );

通過向 _CrtMemDumpStatistics 函數傳遞 _CrtMemState 結構,可以在任意點轉儲該結構的內容:

1 _CrtMemDumpStatistics( &s1 );

若要確定代碼中某一部分是否發生了內存泄漏,可以在該部分之前和之後對內存狀態拍快照,然後使用 _CrtMemDifference 比較這兩個狀態:

#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;
}

調試時,程序輸出如下結果:

image

這說明在s1和s2之間存在內存泄漏!!!如果GetMemory不是在s1和s2之間調用,那麼就不會有信息輸出。


Linux平臺下的內存泄漏檢測

在上面我們介紹了,vs中在代碼中“包含crtdbg.h,將 malloc 和 free 函數映射到它們的調試版本,即 _malloc_dbg 和 _free_dbg,這兩個函數將跟蹤內存分配和釋放。 此映射只在調試版本(在其中定義了_DEBUG)中發生。 發佈版本使用普通的 malloc 和 free 函數。”即爲malloc和free做了鉤子,用於記錄內存分配信息。

Linux下面也有原理相同的方法——mtrace,http://en.wikipedia.org/wiki/Mtrace。方法類似,我這就不具體描述,參加給出的鏈接。這節我主要介紹一個非常強大的工具valgrind。如下圖所示:

image

如上圖所示知道:

==6118== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1 
==6118==    at 0x4024F20: malloc (vg_replace_malloc.c:236) 
==6118==    by 0x8048724: GetMemory(char*, int) (in /home/netsky/workspace/a.out) 
==6118==    by 0x804874E: main (in /home/netsky/workspace/a.out)

是在main中調用了GetMemory導致的內存泄漏,GetMemory中是調用了malloc導致泄漏了100字節的內存。

Things to notice: 
• There is a lot of information in each error message; read it carefully. 
• The 6118 is the process ID; it’s usually unimportant. 
• The first line ("Heap Summary") tells you what kind of error it is. 
• Below the first line is a stack trace telling you where the problem occurred. Stack traces can get quite large, and be 
confusing, especially if you are using the C++ STL. Reading them from the bottom up can help.

• The code addresses (eg. 0x4024F20) are usually unimportant, but occasionally crucial for tracking down weirder 
bugs.

The stack trace tells you where the leaked memory was allocated. Memcheck cannot tell you why the memory leaked, 
unfortunately. (Ignore the "vg_replace_malloc.c", that’s an implementation detail.) 
There are several kinds of leaks; the two most important categories are: 
• "definitely lost": your program is leaking memory -- fix it! 
• "probably lost": your program is leaking memory, unless you’re doing funny things with pointers (such as moving 
them to point to the middle of a heap block)

Valgrind的使用請見手冊http://valgrind.org/docs/manual/manual.html


2.使用VLD工具

下載vld:http://vld.codeplex.com/releases/view/82311

         http://www.codeproject.com/tools/visualleakdetector.asp

使用 解壓之後得到vld.h, vldapi.h, vld.lib, vldmt.lib, vldmtdll.lib, dbghelp.dll等文件。將.h文件和.lib文件拷貝到你要檢測的工程文件所在的目錄裏(只針對此工程),將dbghelp.dll拷貝到你的程序的運行目錄下。

用法:在包含入口函數的.cpp文件中加入語句#include "vld.h" #pragma comment(lib, "vld.lib") 即可。
編譯正確後,在debug方式下運行:查看VC的輸出信息:
有內存泄露顯示:"WARNING: Visual Leak Detector detected memory leaks!"
沒有內存泄露,此輸出的信息爲:"No memory leaks detected."

//#define _CRTDBG_MAP_ALLOC
#include <stdio.h>
#include <string.h>
#include <iostream>
//#include <crtdbg.h>

#include "vld.h"
#pragma comment(lib, "vld.lib")

using std::cout;
using std::endl;
void GetMemory(char *p, int num)
{
    p = (char*)malloc(sizeof(char)* num);
    int* ip = new int;
}

int main()
{
    char *str = NULL;
    GetMemory(str, 100);
    cout << "Memory leak test!" << endl;
    //_CrtDumpMemoryLeaks();
    return 0;
}
輸出:

Visual Leak Detector Version 1.0 installed (multithreaded DLL).
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 153 at 0x01165528: 4 bytes ----------
  Call Stack:
    0x0F39D626 (File and line number not available): free_dbg
    0x0F39DBCD (File and line number not available): msize_dbg
    0x0F39DB7A (File and line number not available): msize_dbg
    0x0F39E5A9 (File and line number not available): malloc
    0x0F38C26F (File and line number not available): operator new
    e:\my projects\cpp\hellovs2013\hellovs2013\helloworld.cpp (21): GetMemory
    e:\my projects\cpp\hellovs2013\hellovs2013\helloworld.cpp (30): main
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (626): __tmainCRTStartup
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (466): mainCRTStartup
    0x770B495D (File and line number not available): BaseThreadInitThunk
    0x778398EE (File and line number not available): RtlInitializeExceptionChain
    0x778398C4 (File and line number not available): RtlInitializeExceptionChain
  Data:
    CD CD CD CD                                                  ........ ........
---------- Block 152 at 0x0116FE88: 100 bytes ----------
  Call Stack:
    0x0F39D626 (File and line number not available): free_dbg
    0x0F39DBCD (File and line number not available): msize_dbg
    0x0F39DB7A (File and line number not available): msize_dbg
    0x0F39E5A9 (File and line number not available): malloc
    e:\my projects\cpp\hellovs2013\hellovs2013\helloworld.cpp (20): GetMemory
    e:\my projects\cpp\hellovs2013\hellovs2013\helloworld.cpp (30): main
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (626): __tmainCRTStartup
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (466): mainCRTStartup
    0x770B495D (File and line number not available): BaseThreadInitThunk
    0x778398EE (File and line number not available): RtlInitializeExceptionChain
    0x778398C4 (File and line number not available): RtlInitializeExceptionChain
  Data:
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD                                                  ........ ........
Visual Leak Detector detected 2 memory leaks.
Visual Leak Detector is now exiting.




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章