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函数是在哪里调用的,可以快速定位到代码,从而进行查找原因