今天著手解決一個現有程式的記憶體泄露問題,以前從沒用過相關檢測的工具、手段!
搜了幾篇文章,感覺還不錯:
主要的偵查工具是debuger和crt堆除錯函數:
http://bbs.17testing.com/viewthread.php?tid=8516 掃盲——記憶體流失的有關知識
http://leoman95.spaces.live.com/blog/cns!87f442ce8fa434b1!131.entry VC++6.0中記憶體流失檢測
http://www.360doc.com/content/081028/22/7635_1840985.html vc記憶體泄露檢測-常用技巧(轉載)
http://fafeng.blogbus.com/logs/7525571.html 記憶體泄露偵查工具
http://www.cppblog.com/bigsml/archive/2006/10/19/13860.html 用BoundChecker檢測記憶體泄露(zz)
http://blueluhan.spaces.live.com/blog/cns!B0E40D35BB595164!1412.entry 使用BoundChecker的好處
【推薦】http://blog.csdn.net/seawen/archive/2009/01/05/3714128.aspx VC記憶體流失偵查工具_Visual Leak Detector
推薦第三方內錯泄露偵查工具:
Purify 很好,很強大,收費
BoundsChecker 很好,很強大,收費
Visual Leak Detector 很好,很淺大,免費,源碼都有,強烈推薦
這裡主要介紹一下用法:
需在檔案中包含以下
標頭檔和宏定義:
#include<stdlib.h>
#include<crtdbg.h>
#define _CRTDBG_MAP_ALLOC
環境:
列印的記憶體訊息只在調試狀態下,output視窗的debug頁輸出。
函數:
_CrtDumpMemoryLeaks(); 報告當前程式記憶體泄露狀況
因為程式可能在多個出口,在main函數入口加上這句:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
即可保證,在程式結束時一定調用一次_CrtDumpMemoryLeaks();
_CrtMemState s1; 聲明一個記憶體快照的結構體資訊
_CrtMemCheckpoint( &s1 ); 將當前記憶體快照儲存到s1中
_CrtMemDumpStatistics(&s1); 列印s1記憶體快照資訊
_CrtMemDifference(&s3, &s1, &s2);將快照s1和s2的差異,儲存到s3中
_crtBreakAlloc(45); 此函數用來在記憶體第45次分配時,產生中斷,也可以在Watch視窗中,
name欄寫“_crtBreakAlloc”, value值寫“45”,同樣會產生中斷!便於調試!
注意:
在vc6.0下,若你的程式包含<iostream.h><string>等標頭檔,你會發現總是會報告記憶體泄露:
Detected memory leaks!
Dumping objects ->
{45} normal block at 0x00372638, 33 bytes long.
Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD
{44} normal block at 0x003725D0, 40 bytes long.
Data: < |L > 14 7C 4C 10 16 00 00 00 00 00 00 00 00 00 00 00
Object dump complete.
這估計是vc6的bug吧!
附上一個測試代碼:
//////////////////////////////////////////////////////////////////////////<br />//功能:vc6.0 Debugger + CRT示範程式<br />//說明:使用該程式,需要在project中設定屬性,將MFC作為共用的DLL<br />//作者:wuming<br />//////////////////////////////////////////////////////////////////////////<br />#include "stdio.h"<br />#include<stdlib.h><br />#include<crtdbg.h><br />#include <afx.h><br />#define _CRTDBG_MAP_ALLOC<br />//定位記憶體泄露來源程式行<br />#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#endif<br />int main()<br />{<br />//設定開關,在程式結束時,自動報告一次記憶體泄露情況<br />_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );<br />//重新導向報告<br />freopen( "out.txt", "w", stderr);<br />_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );<br />_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );<br />_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );<br />_CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );<br />_CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );<br />_CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );<br />_CrtMemState s1, s2, s3;<br />//trace本身也會造成mem leak在vc6.0下<br />TRACE("/n................ Start Main() ................../n");<br />_CrtMemCheckpoint( &s1 );<br />_CrtMemDumpStatistics(&s1);<br />new char[100];<br />TRACE("/n................. Report Mem Leak() ............/n");<br />_CrtDumpMemoryLeaks();</p><p>TRACE("/n................ First ff() ................../n");<br />_CrtMemCheckpoint(&s2);<br />_CrtMemDumpStatistics(&s2);<br />TRACE("/n................ Difference() ................../n");<br />_CrtMemDifference(&s3, &s1, &s2);<br />_CrtMemDumpStatistics(&s3);<br />TRACE("/n.................. The Last Auto Report Mem Leak() ............/n");<br /> return 0;<br />}
第三方工具VLD用法:visual leak detector
現在vld已經有了1.9的beta版,我用的是1.0版本,
1. 下載
解壓後,會有:
三個lib檔案,“vld.lib”,“vldmt.lib”,“vldmtdll.lib”
兩個.h檔案,“vld.h”,“vldapi.h”
一個dll檔案,“dbhelp.dll”
2. 安裝
把lib檔案拷貝到vc6.0安裝目錄“.../Microsoft Visual Studio/VC98/Lib”下,
然後把.h檔案拷貝到自己的工程目錄下,
3. 使用
你工程的源碼裡,在程式入口檔案處,第一個include那兩個.h檔案,若檔案有“stdafx.h”,則
在include "stdafx.h"後,再包含兩個檔案。
4. 報告輸出
vld是在調式狀態下,才會進行記憶體檢測,最終的報告也輸出在output視窗的debug頁。
對於非調試執行程式,vld不作任何檢查,這樣,及時發布程式,也無需注釋掉vld,不影響效能!
5. 注意
若是發現報告內容都是地址碼,沒有函數名路徑資訊,則拷貝dbhelp.dll到工程的debug目錄下即可!
附錄:記憶體流失的發生方式(摘自: http://fafeng.blogbus.com/logs/7525571.html)
1.常發性記憶體流失。發生記憶體流失的代碼會被多次執行到,每次被執行的時候都會導致一塊記憶體流失。
2.偶發性記憶體流失。發生記憶體流失的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測記憶體流失至關重要。
3.一次性記憶體流失。發生記憶體流失的代碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊且僅有一塊記憶體發生泄漏。
4.隱式記憶體流失。程式在運行過程中不停的分配記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡並沒有發生記憶體流失,因為最終程式釋放了所有申請的記憶體。但是對於一個伺服器程式,需要運行幾天,幾周甚至幾個月,不及時釋放記憶體也可能導致最終耗盡系統的所有記憶體。所以,我們稱這類記憶體流失為隱式記憶體流失。