菜鸟也能搞定C++内存泄漏
?菜鳥也能搞定C++內(nèi)存泄漏
背景
C++內(nèi)存分配與釋放均由用戶代碼自行控制,靈活的機制有如潘多拉之盒,即讓程序員有了更廣的發(fā)揮空間,也產(chǎn)生了代代相傳的內(nèi)存泄漏問題。對于新手來說,最常犯的錯誤就是new出一個對象而忘記釋放,對于一般小應(yīng)用程序來說,一點內(nèi)存空間不算什么。但是當(dāng)內(nèi)存泄漏問題出現(xiàn)在需要24小時運行的平臺類程序上的時候,將會使系統(tǒng)可用內(nèi)存飛速減少,最后耗盡系統(tǒng)資源,導(dǎo)致系統(tǒng)崩潰。
所以學(xué)會如何防止并檢查內(nèi)存泄漏,是一個合格的c++程序員必須具備的能力。但是由于內(nèi)存泄漏是程序運行并滿足一定條件時才會發(fā)生,直接從代碼中查出泄漏原因的難度較大,而且一旦內(nèi)存泄漏發(fā)生在多線程程序中,從大量的代碼中要靠人工找出泄漏原因,無論對新人還是老手都是一場噩夢。
本文介紹一種在vs2003中檢查內(nèi)存泄漏的方法,供各位新人老手參考,在vc6中實現(xiàn)需要做一些變動,詳情可自行參照相關(guān)資料。
檢查策略分析
首先,假定我們需要檢測一個24小時運行的平臺程序的內(nèi)存泄漏情況,我們無法確定具體的內(nèi)存泄漏速度,但是我們可以確定該程序在一定時間內(nèi)(如10分鐘)泄漏的內(nèi)存量是接近的,設(shè)為L(eak)。
考慮在10分鐘的運行時間內(nèi)程序新申請到的內(nèi)存A(lloc),這部分內(nèi)存其實包含了程序運行正常申請,并會在后續(xù)運行中進行釋放的普通內(nèi)存塊N(ormal)和泄漏的內(nèi)存L,即:
A = N + L
在后續(xù)的運行中,由于N部分不斷的申請和釋放,所以這部分的總量基本上是不變的,而L部分由于只申請而不釋放,占用的內(nèi)存總量將會越來越大。
將這個結(jié)果放到運行時間軸上,現(xiàn)在我們觀察程序運行中的20分鐘,我們假定內(nèi)存泄漏速度為dL/10分鐘,時間軸如下:
----------------|--------------------|-------------------|----------------------------
Tn-2 Tn-1 Tn
三點間隔均為10分鐘,則我們有如下結(jié)論:
Tn點總的內(nèi)存分配量 An = N + dL * n,N為正常分配內(nèi)存,dL*n為內(nèi)存泄漏量的總和,而Tn-1點的內(nèi)存總量則為 An-1 = N + dL*(n-1)。注意,我們這里不考慮釋放的內(nèi)存量,僅考慮增加的內(nèi)存量。因此很明顯單位時間內(nèi)的內(nèi)存泄漏量 dL = An - An-1。
生成內(nèi)存Dump文件的代碼實現(xiàn)
要完成如上的策略,我們首先需要能跟蹤內(nèi)存塊的分配與釋放情況,并且在運行時將分配情況保存到文件中,以便進行比較分析,所幸m$已經(jīng)為我們提供了一整套手段,可以方便地進行內(nèi)存追蹤。具體實現(xiàn)步驟如下:
包含內(nèi)存追蹤所需庫
在StdAfx.h中添加如下代碼,注意必須定義宏_CRTDBG_MAP_ALLOC,否則后續(xù)dump文件將缺少內(nèi)存塊的代碼位置。
#ifdef _DEBUG
//for memory leak check
#define _CRTDBG_MAP_ALLOC //使生成的內(nèi)存dump包含內(nèi)存塊分配的具體代碼為止
#include<stdlib.h>
#include<crtdbg.h>
#endif
啟動內(nèi)存追蹤
上述步驟完成后,則可以在應(yīng)用程序啟動處添加如下代碼,啟動內(nèi)存追蹤,啟動后程序?qū)⒆詣訖z測內(nèi)存的分配與釋放情況,并允許將結(jié)果輸出。
//enable leak check
_CrtSetDbgFlag( _CRTDBG_REPORT_FLAG);
將結(jié)果輸出指向dump文件
由于默認(rèn)情況下,內(nèi)存泄漏的dump內(nèi)容是輸出到vs的debug輸出窗口,但是對于服務(wù)類程序肯定沒法開著vs的debug模式來追蹤內(nèi)存泄漏,所以必須將dump內(nèi)容的輸出轉(zhuǎn)到dump文件中。在程序中添加如下部分:
HANDLE hLogFile;//聲明日志文件句柄
hLogFile = CreateFile("./log/memleak.log", GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//創(chuàng)建日志文件
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);//將warn級別的內(nèi)容都輸出到文件(注意dump的報告級別即為warning)
_CrtSetReportFile(_CRT_WARN, hLogFile);//將日志文件設(shè)置為告警的輸出文件
保存內(nèi)存Dump
完成了以上的設(shè)置,我們就可以在程序中添加如下代碼,輸出內(nèi)存dump到指定的dump文件中:
_CrtMemState s1, s2, s3;//定義3個臨時內(nèi)存狀態(tài)
......
_CrtDumpMemoryLeaks();//Dump從程序開始運行到該時刻點,已分配而未釋放的內(nèi)存,即前述An
//以下部分非必要,僅為方便后續(xù)分析增加信息
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )
{
_CrtMemDumpStatistics( &s3 );//dump相鄰時間點間的內(nèi)存塊變化
//for next compare
_CrtMemCheckpoint( &s1 );
}
time_t now = time(0);
struct tm *nowTime = localtime(&now);
_RPT4(_CRT_WARN,"%02d %02d:%02d:%02d snapshot dump./n",
nowTime->tm_mday, nowTime->tm_hour,nowTime->tm_min,nowTime->tm_sec);//輸出該次dump時間
以上代碼最好放在一個函數(shù)中由定時器定期觸發(fā),或者手動snapshot生成相等時間段的內(nèi)存dump。
dump文件內(nèi)容示例如下:
Detected memory leaks!
Dumping objects ->
{20575884} normal block at 0x05C4C490, 87 bytes long.
Data: < > 02 00 1D 90 84 9F A6 89 00 00 00 00 00 00 00 00
...
d:/xxxxx/xxxworker.cpp(903) : {20575705} normal block at 0x05D3EF90, 256 bytes long.
Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...
Object dump complete.
0 bytes in 0 Free Blocks.
215968 bytes in 876 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 220044 bytes.
Total allocations: 7838322 bytes.
10 16:29:14 snapshot dump.
上面紅色部分即為用戶代碼中分配而未釋放的內(nèi)存塊位置。
解析Dump文件
前面我們已經(jīng)通過dump文件獲取到各時刻點的內(nèi)存dump,根據(jù)前面的分析策略,我們只需要將第n次dump的內(nèi)存塊分配情況An,與第n-1次dump內(nèi)存塊分配情況An-1作比較,即可定位到發(fā)生內(nèi)存泄漏的位置。由于dump文件一般容量巨大,靠人工進行對比幾乎不可能,所以僅介紹比較的思路,各位需要自行制作小工具進行處理。
1、提取兩個相鄰時間點的dump文件D1和D2,設(shè)D1是D2之前的dump
2、各自提取dump文件中用戶代碼分配的內(nèi)存塊(即有明確代碼位置,而且為normal block的內(nèi)存塊),分別根據(jù)內(nèi)存塊ID(如“d:/xxxxx/xxxworker.cpp(903) : {20575705}”紅色部分)保存在列表L1和L2
3、遍歷列表L2,記錄內(nèi)存塊ID沒有在L1中出現(xiàn)過的內(nèi)存塊,這些內(nèi)存塊即為可能泄漏的內(nèi)存
4、根據(jù)3的結(jié)果,按照內(nèi)存的分配代碼位置,統(tǒng)計各處代碼泄漏的內(nèi)存塊個數(shù),降序排列,分配次數(shù)越多的代碼,內(nèi)存泄漏可能性越大。
總結(jié)
以上是生活随笔為你收集整理的菜鸟也能搞定C++内存泄漏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux用户和群组的作用,linux中
- 下一篇: 深入研究 C++中的 STL Deque