windows記憶體泄露定位方法
記憶體泄露(Memory Leak)是C/C++程式經常遇到的一個棘手問題,簡單來說,記憶體泄露就是沒有釋放本來應該釋放的記憶體,
可以把解決記憶體泄露問題分成兩步,第一步是定位到泄露的堆塊,第二步是定位到泄露堆塊是哪段代碼分配的,本文介紹如何使用CRT堆的除錯支持來實作這兩個目標,
1._CrtDumpMemoryLeaks
CRT設計了一個名為_CrtDumpMemoryLeaks的函式來檢測和報告發生在堆上的記憶體泄露,
呼叫后產生類似如下的資訊:
執行緒 0x1c98 已退出,回傳值為 0 (0x0),
Detected memory leaks!
Dumping objects ->
D:\cpp\vs2019\memleak\memleak\memleak.cpp(29) : {87} normal block at 0x00890838, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
D:\cpp\vs2019\memleak\memleak\memleak.cpp(26) : {86} normal block at 0x008907F8, 20 bytes long.
Data: <beijing2013 > 62 65 69 6A 69 6E 67 32 30 31 33 00 CD CD CD CD
Object dump complete.
程式“[14228] memleak.exe”已退出,回傳值為 0 (0x0),
2.何時呼叫
上面的函式,無論何時呼叫,都會將當前堆中未是放過的塊認為是泄露而轉儲出來,為了防止把將來能正常釋放的塊誤當作記憶體泄露,我們必須選擇合適的時機來呼叫這個函式,太早呼叫,會誤判,
何時呼叫呢?CRT的退出函式(exit 和 doexit)會在執行完終結器(包含全域物件析構)后,再呼叫_CrtDumpMemoryLeaks
只要設定一個標記就會激活這個函式檢查:
_crtDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
3.定位導致泄露的原始碼
定義一個標記_CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
注意順序不能錯,這個標記影響crtdbg.h的編譯,從而影響malloc的函式定義,
這樣處理后,malloc就可以列印行號了,但new還不行,
需要定義除錯版本的new
#ifdef _DEBUG
#define MYDEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__)
#define new MYDEBUG_NEW
#endif// _DEBUG
這樣就可以列印完整了,
4.快速找到泄露
觀察轉儲列印:
D:\cpp\vs2019\memleak\memleak\memleak.cpp(26) : {86} normal block at 0x008907F8
其中的{86} 代表第86次分配記憶體操作發生了泄露,你可能要說,我只new過一次,怎么會是第86次?這很容易理解,其他的記憶體申請操作在C的初始化程序呼叫的唄,:)
有沒有可能,我們讓程式運行到第52次記憶體分配操作的時候,自動停下來,進入除錯狀態?所幸,crtdbg確實提供了這樣的函式:即 long _CrtSetBreakAlloc(long nAllocID),我們加上它:
_crtBreakAlloc = 86;
這只這個全域變數即可,
你發現,程式運行到 p1 = (char*)new char[20]; 一句時,自動停下來進入除錯狀態,細細體會一下,你可以發現,這種方式你獲得的資訊遠比在程式退出時獲得檔案名及行號有價值得多,因為報告泄漏檔案名及行號,你獲得的只是靜態的資訊,然而_CrtSetBreakAlloc則是把整個現場恢復,你可以通過對函式呼叫堆疊分析(我發現很多人不習慣看函式呼叫堆疊,如果你屬于這種情況,我強烈推薦你去補上這一課,因為它太重要了)以及其他在線除錯技巧,來分析產生記憶體泄漏的原因,通常情況下,這種分析方法可以在5分鐘內找到肇事者,
當然,_CrtSetBreakAlloc要求你的程式執行程序是可還原的(多次執行程序的記憶體分配順序不會發生變化),這個假設在多數情況下成立,不過,在多執行緒的情況下,這一點有時難以保證,
5. 程式原始碼:--來自軟體除錯這本書
#include <stdio.h>
#include <string.h>
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <malloc.h>
#ifdef _DEBUG
#define MYDEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__)
#define new MYDEBUG_NEW
#endif// _DEBUG
#pragma warning(disable : 4996)
int main()
{
char* p1, * p2;
printf("Sample to demo memory leak checking of RTC by Raymond!\n");
_crtDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
_crtBreakAlloc = 86;
p1 = (char*)new char[20];
strcpy(p1, "beijing2013");
p2 = (char*)malloc(100);
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/272590.html
標籤:其他
下一篇:Linux--檔案系統
