C++內存泄露一直是個頭痛的問題,但是總要解決吧,在網上搜了很久以後,終於找到了一個不是辦法的辦法,這個辦法有缺陷,但是有總比沒有強吧!
使用的編譯器:VS2010。
這裏需要說點彙編的知識:
void fun(int nVal)
{
}
當調用函數fun(a)時,首先a會入棧,其次是返回地址會入棧,我們可以重載operator new和operator delete來記錄每個調用的地址,然後用鏈表list1記錄new得到的內存塊,用鏈表list2記錄delete釋放的內存塊,最後把這兩個鏈表遍歷,比較他的的值,值相同的去掉,剩下不同的值則是沒有釋放的內存。
0x00414BBC fun(a);
0x00414BC1
調用fun(a)時,a會入棧,然後會把返回地址(0x00414BC1)入棧。他們的內存是相鄰的。所以我們可以把返回地址存儲起來,我們就可以知道那段代碼調用了這個函數。在VS2010中,調試的時候,右鍵,轉到反彙編,定位到0x00414BC1處,就可以看到。
struct MenInfo
{
unsigned int nNewCalAddress;//new函數調用地址。
unsigned in nDeleteCallAddress;//delete函數調用地址。
unsigned int nTotalSize;//內存總大小。我們需要額外的內存來存儲一些信息,例如調用地址。
unsigned int nUseSize;//使用大小。
};
list<void *> list1,
list<void *> list2;
//其實new只有一個參數,但是VS定義了new宏(大概是爲了調試方便吧),所以變成了3個參數,
void *::operator new(unsigned int size, const char *file, int line)
{
//參數入棧的順序是從右到左,即最後入棧的是變量size
////函數返回的地址的地址等於最後入棧變量地址(這裏是size)-4
void **funAddr = (void **)(((int)&size) - 4);//函數返回地址的地址 unsigned int nFunCallAddress = (*(int *)funAddr);//函數的返回地址
void *pBuf = NULL; int nTotalSize = size + sizeof(MemInfo);//我們需要多分配一點內存來存儲MenInfo try { pBuf = malloc(nTotalSize); } catch (...) { pBuf = NULL; return NULL; }
MemInfo *pMemInfo = (MemInfo *)pBuf; pMemInfo->nNewCallAddress = nFunCallAddress; pMemInfo->nDeleteCallAddress = NULL;
pMemInfo->nTotalSize = nTotalSize; pMemInfo->nUseSize = size;
void *buffer = ((char *)pBuf + sizeof(MemInfo));
memset(buffer, 0, size);
//list1.push_back(buffer);
return buffer;
}
void ::operator delete(void *buf)
{
void **funAddr = (void **)(((int)&buf) - 4);//返回地址的地址
unsigned int nFunCallAddress = (*(int *)funAddr);//得到返回地址
MemInfo *pMemInfo = (MemInfo *)((char *)buf - sizeof(MemInfo)); pMemInfo->nDeleteCallAddress = nFunCallAddress;
memset(buf, 0, pMemInfo->nUseSize);
//沒有釋放內存,我們需要內存塊裏面的信息(new函數返回地址,delete函數返回地址),所以只能用於調試。
//list2.push_back(buf);
}
最後是要遍歷list1和list2進行比較,就知道哪些內存沒有釋放,而且從內存塊裏我沒可以知道new函數是在哪裏調用的,可以快速定位到代碼,從而進行查找原因