從沒有被釋放的記憶體塊,可以獲得文件名、行號,泄漏多少位元組,會列印顯示出來。 ...
理論
- 什麼是記憶體泄露:指因為疏忽或錯誤造成程式未能釋放已經不再使用的記憶體的情況。記憶體泄漏並不是指記憶體在物理上的消失,而是應用程式分配某段記憶體後,因為設計錯誤,失去了對該段記憶體的控制,因而造成了記憶體的浪費。
工具作用
- 在使用Debug版的malloc分配記憶體時,malloc會在記憶體塊的頭中記錄分配該記憶體的文件名及行號。
- 當程式退出時會在main()函數返回之後做一些清理工作,這個時候來檢查調試堆記憶體,如果仍然有記憶體沒有被釋放,則一定是存在記憶體泄漏。
- 從沒有被釋放的記憶體塊,就可以獲得文件名、行號,泄漏多少位元組,會列印顯示出來。
- 如下圖所示,記憶體泄漏檢測工具運行的效果。
Debug調試常用的巨集
__FILE__ //所在的文件
__FUNCTION__ //函數功能
__FUNCDNAME__ //函數名
__LINE__ //在第幾行
__DATE__ //產生的日期
__TIME__ //產生的時間
代碼實現
MemoryCheck.h
#pragma once
#ifndef __MEMORY_CHECK_H__
#define __MEMORY_CHECK_H__
#ifdef _DEBUG
void* operator new(size_t size, const char* filename, const char* funame, int line);
void* operator new[](size_t size, const char* filename, const char* funame, int line);
void operator delete(void* pMem);
void operator delete[](void* pMem);
#ifndef __USE_MEM_CHECK__
#define __USE_MEM_CHECK__
#define new new(__FILE__,__FUNCTION__,__LINE__)
#endif // !__USE_MEM_CHECK__
#endif // _DEBUG
#endif // !__MEMORY_CHECK_H__
MemoryCheck.cpp
#include<map>
#include<iostream>
#include<Windows.h>
#define __USE_MEM_CHECK__
#include"MemoryCheck.h"
typedef struct stMemInfo
{
void* pMem;
size_t size;
int line;
char funcname[256];
char filename[256];
}MEMINFO,*LPMEMINFO;
std::map<void*, LPMEMINFO>g_MemMap;//存儲記憶體分配的信息
typedef std::map<void*, LPMEMINFO>MEMMAP;
typedef MEMMAP::iterator MEMMAPItr;
class CMemMgr
{
public:
static CMemMgr& Instance()
{
static CMemMgr instance;
return instance;
}
void* Push(LPMEMINFO pInfo)
{
g_MemMap[pInfo->pMem] = pInfo;
return pInfo->pMem;
}
void Pop(void* pMem)
{
MEMMAPItr it = g_MemMap.find(pMem);
if (it != g_MemMap.end())
{
free(pMem);
free(it->second);
g_MemMap.erase(it);
}
}
~CMemMgr()
{
if (!g_MemMap.empty())
{
OutputDebugStringA("\n----------------------------------發現記憶體泄露信息----------------------------------\n\n");
char buf[256] = {};
int count = 0;
for (auto it:g_MemMap)
{
sprintf_s(buf, "【記憶體泄漏警告 %d 】 文件%s,第%d行的函數%s中泄漏了%d個位元組的記憶體\n",
count++,
it.second->filename,
it.second->line,
it.second->funcname,
it.second->size);
OutputDebugStringA(buf);
free(it.second->pMem);
free(it.second);
}
g_MemMap.clear();
OutputDebugStringA("\n-------------------------------記憶體泄漏檢測結束----------------------------------\n\n");
}
}
private:
CMemMgr() {};
CMemMgr& operator=(const CMemMgr&) = delete;
CMemMgr(const CMemMgr&) = delete;
};
void* operator new(size_t size, const char* filename, const char* funcname, int line)
{
LPMEMINFO pInfo=(LPMEMINFO)malloc(sizeof(MEMINFO));
pInfo->size = size;
pInfo->line = line;
pInfo->pMem = malloc(size);
strcpy_s(pInfo->filename, filename);
strcpy_s(pInfo->funcname, funcname);
return CMemMgr::Instance().Push(pInfo);
}
void* operator new[](size_t size, const char* filename, const char* funcname, int line)
{
return operator new(size, filename, funcname, line);
}
void operator delete(void* pMem)
{
CMemMgr::Instance().Pop(pMem);
}
void operator delete[](void* pMem)
{
operator delete(pMem);
}
main.cpp
#include<iostream>
#include"MemoryCheck.h"
int main()
{
int* p = new int;
int* p2 = new int[5];
system("pause");
return 0;
}
註意
- 重載的new相衝突解決辦法:
- 1.使用巨集定義開關#define USE_MEM_CHECK,定義在MemoryCheckr.h的前面才能實現巨集定義開關,註意順序很重要,順序錯誤也會導致不識別。
- 2.使用#undef new也可解決