C++中的內存泄露通常指堆中的內存泄露。堆內存是咱們手動malloc/realloc/new申請的,程序不會自動回收,須要調用free或delete手動釋放,不然就會形成內存泄露。內存泄露其實還應該包括系統資料的泄露,好比socket鏈接等,使用完後也要釋放。ios
內存泄露的緣由:c++
總結下來,內存泄露大概有一下幾個緣由:web
一、編碼錯誤:malloc、realloc、new申請的內存在堆上,須要手動顯示釋放,調用free或delete。申請和釋放必須成對出現malloc/realloc對應free,new對應delete。前者不會運行構造/析構函數,後者會。對於C++內置數據類型可能沒差異,可是對於本身構造的類,可能在析構函數中釋放系統資源或釋放內存,因此要對應使用。緩存
二、「無主」內存:申請內存後,指針指向內存的起始地址,若丟失或修改這個指針,那麼申請的內存將丟失且沒有釋放。服務器
三、異常分支致使資源未釋放:程序正常執行沒有問題,可是若是遇到異常,正常執行的順序或分支會被打斷,得不到執行。因此在異常處理的代碼中,要確保系統資源的釋放。socket
四、隱式內存泄露:程序運行中不斷申請內存,可是直到程序結束才釋放。有些服務器會申請大量內存做爲緩存,或申請大量Socket資源做爲鏈接池,這些資源一直佔用直到程序退出。服務器運行起來通常持續幾個月,不及時釋放可能會致使內存耗盡。函數
五、類的析構函數爲非虛函數:析構函數爲虛函數,利用多態來調用指針指向對象的析構函數,而不是基類的析構函數。編碼
內存泄露的檢測spa
內存泄露的關鍵就是記錄分配的內存和釋放內存的操做,看看能不能匹配。跟蹤每一塊內存的聲明週期,例如:每當申請一塊內存後,把指向它的指針加入到List中,當釋放時,再把對應的指針從List中刪除,到程序最後檢查List就能夠知道有沒有內存泄露了。Window平臺下的Visual Studio調試器和C運行時(CRT)就是用這個原理來檢測內存泄露。指針
在VS中使用時,需加上
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
crtdbg.h的做用是將malloc和free函數映射到它們的調試版本_malloc_dbg和_free_dbg,這兩個函數將跟蹤內存分配和釋放(在Debug版本中有效)
_CrtDumpMemoryLeaks();
函數將顯示當前內存泄露,也就是說程序運行到此行代碼時的內存泄露,全部未銷燬的對象都會報出內存泄露,所以要讓這個函數儘可能放到最後。
例如:
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
char *str1 = NULL;
char *str2 = NULL;
str1=new char[100];
str2=new char[50];
delete str1;
_CrtDumpMemoryLeaks();
return 0;
}
上述代碼中,內存申請了兩塊,可是隻釋放了一塊,運行調試,會在output窗口輸出:
Dumping objects ->
{136} normal block at 0x00084D70, 50 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
能夠看到會檢測到內存泄露。 可是並無檢測到泄露內存申請的位置,已經加了宏定義#define _CRTDBG_MAP_ALLOC。緣由是申請內存用的是new,而剛剛包含頭文件和加宏定義是重載了malloc函數,並無重載new操做符,因此要本身定義重載new操做符才能檢測到泄露內存的申請位置。修改以下:
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG //重載new
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
char *str1 = NULL;
char *str2 = NULL;
str1=(char*)malloc(100);
str2=new char[50];
_CrtDumpMemoryLeaks();
return 0;
}
運行結果:
Detected memory leaks!
Dumping objects ->
e:\c++\test\內存泄露檢測2\main.cpp(13) : {62} normal block at 0x001714F8, 50 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
e:\c++\test\內存泄露檢測2\main.cpp(12) : {61} normal block at 0x00171458, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
能夠看到
main.cpp()括號裏面的數字就是泄露內存的起始位置。那麼後面的{62} normal block at 0x001714F8, 50 bytes long.
表明什麼?
大括號{}裏面的數字表示第幾回申請內存操做;0x001714F8表示泄露內存的起始地址,CD CD表示泄露內存的內容。
爲何是第62次申請內存,由於在初始化操做時也申請了內存。經過這個信息,能夠設置斷點。調用long _CrtSetBreakAlloc(long nAllocID)能夠再第nAllocID次申請內存是中斷,在中斷時獲取的信息比在程序終止時獲取的信息要多,你能夠調試,查看變量狀態,對函數調用調試分析,解決內存泄露。
block分爲3中類型,此處爲normal,表示普通,此外還有client表示客戶端(專門用於MFC),CRT表示運行時(有CRT庫來管理,通常不會泄露),free表示已經釋放掉的塊,igore表示要忽略的塊。
在上面程序中,調用_CrtDumpMemoryLeaks()來檢測內存泄露,若是程序可能在多個地方終止,必須在多個地方調用這個函數,這樣比較麻煩,能夠在程序起始位置調用_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ),這樣不管程序什麼時候終止,都會在終止前調用_CrtDumpMemoryLeaks()。
除此以外,還能夠在某時刻設置檢查點,獲取當時內存狀態的快照。比較不一樣時刻內存狀態的差別。
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG //重載new
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
_CrtMemState s1, s2, s3;
char *str1 = NULL;
char *str2 = NULL;
str1=(char*)malloc(100);
_CrtMemCheckpoint( &s1 );//記錄內存快照
_CrtMemDumpStatistics( &s1 );//輸出
str2=new char[50];
_CrtMemCheckpoint( &s2 );
_CrtMemDumpStatistics( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )//比較s1和s2,把比較結果輸出到s3
_CrtMemDumpStatistics( &s3 );// dump 差別結果
return 0;
}
輸出結果爲:
0 bytes in 0 Free Blocks.
100 bytes in 1 Normal Blocks.
8434 bytes in 54 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 8963 bytes.
Total allocations: 14003 bytes.
0 bytes in 0 Free Blocks.
150 bytes in 2 Normal Blocks.
8434 bytes in 54 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 8963 bytes.
Total allocations: 14053 bytes.
0 bytes in 0 Free Blocks.
50 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 50 bytes.
也能夠用此法更復雜檢測內存泄露,例如設置檢查點,檢查檢查點之間的內存泄露。
在Linux下也有相似的方法,具體能夠參考:http://en.wikipedia.org/wiki/Mtrace