通过重载新运算符来查找 MFC C++应用中的内存泄漏
Finding memory leaks in MFC C++ app by overloading new operators
我有一个MFC应用程序。我想跟踪每个动态内存分配(在堆上(,以便能够找出该应用程序中内存泄漏的来源。IDE是Visual Studio 2010。
我做了以下工作:
- 引入了名为"MEMORY_LEAK_FINDER"的预处理器指令。
- 添加了一个名为"CMemLeakHunter"的类,您可以找到确切的内容这些文件如下。
- 这个想法是使每个新运算符过载(所有其中 3 个:new、new[] 和 CObject::new(,并使用它们来跟踪分配内存的位置(文件、行(。在结尾我想将内存泄漏的位置带到使用"CMemoryState"类输出,因此我最终可以将分配跟踪与CMemoryState的比较(差异(跟踪进行比较。
问题是,应用程序编译(在VS 2010调试模式下(,但发生以下链接器错误:
错误4 错误 LNK2005:"void * __cdecl运算符 new[](无符号整数,字符常量 *,int("(??_U@YAPAXIPBDH@Z( 已在 CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj( 中定义 错误 3 错误 LNK2005:"void * __cdecl运算符 new(无符号 int,char const *,int("(??2@YAPAXIPBDH@Z( 已在 CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj( 中定义 错误 5 错误 LNK2005:"public: static void * __stdcall CObject::operator new(unsigned int,char const *,int(" (??2CObject@@SGPAXIPBDH@Z( 已在 CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj( 中定义 错误 6 错误 LNK1169:找到一个或多个乘法定义的符号 E:\软件\模块 1.exe 1
我用谷歌搜索并发现,忽略Nafxcwd.lib库可能会解决问题。在我的应用程序中不是,我尝试了,但忽略了该库,另一个 17000 链接器错误(未解析的外部(。
其他依赖项包括:Nafxcwd.lib;Ws2_32.lib;Version.lib
忽略特定的默认库是:msvcprtd.lib;libcimtd.lib;libcmt.lib
不能如此轻松地拆分软件,因此我寻求帮助:如果我使用 MFC 并且需要使用上面提到的 .lib 文件,我如何跟踪我自己的应用程序完成的内存分配?解决方案是什么?请帮助我解决此问题,以便能够跟踪内存分配以找出泄漏的可能来源。如果他们能够做到这一点,我也愿意使用另一个 MFC 内置例程。但是,我自己没有发现任何有用的东西。
头文件 CMemLeakHunter.hpp 编写如下:
#ifndef _MEM_LEAK_HUNTER_
#define _MEM_LEAK_HUNTER_
#ifdef MEMORY_LEAK_FINDER
#pragma message("Macro MEMORY_LEAK_FINDER is active, overloading new...")
#include "stdafx.h"
#include <map>
using std::map;
#undef new
void* operator new(size_t size, LPCSTR file, int line);
void* operator new[](size_t size, LPCSTR file, int line);
#define new new(__FILE__, __LINE__)
namespace
{
static const size_t LOG_BUFFER_SIZE = 2;
static const size_t DEFAULT_BUFFER_LINE_SIZE = 512;
}
class CMemLeakHunter
{
public:
static CMemLeakHunter& singleton();
void startMemoryTrace(const char* leakPath, const char* allocPath);
void endMemoryTrace();
void addMemory(void* area, size_t size, LPCSTR file, int line);
void deleteMemory(void* area, LPCSTR file, int line);
private:
void flushAllocLog();
void filterTrace();
map<DWORD, size_t> mMemChunks;
CString sLogBuffer[LOG_BUFFER_SIZE];
size_t nLogBufferLines;
CMemoryState oldMemState;
CMemoryState newMemState;
CMemoryState diffMemState;
CString sMemLeakTracePath;
CString sMemAllocTracePath;
static CMutex s_oObjMutex;
};
#endif // MEMORY_LEAK_FINDER
#endif // _MEM_LEAK_HUNTER_
源文件 CMemLeakHunter.cpp 编写如下:
#include "CMemLeakHunter.hpp"
#ifdef MEMORY_LEAK_FINDER
#pragma message("Memory-Leak finder is activated, building functions for the class...")
#undef new
void* operator new(size_t size, LPCSTR file, int line)
{
void* pArea = ::operator new(size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
void* operator new[](size_t size, LPCSTR file, int line)
{
void* pArea = ::operator new[](size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
void* PASCAL CObject::operator new(size_t size, LPCSTR file, int line)
{
void* pArea = CObject::operator new(size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
CMutex CMemLeakHunter::s_oObjMutex;
CMemLeakHunter& CMemLeakHunter::singleton()
{
static CMemLeakHunter theSingleObject;
return theSingleObject;
}
void CMemLeakHunter::startMemoryTrace(const char* leakPath, const char* allocPath)
{
sMemLeakTracePath = leakPath;
sMemAllocTracePath = allocPath;
oldMemState.Checkpoint();
for(size_t i=0; i<LOG_BUFFER_SIZE; i++)
{
sLogBuffer[i] = CString(' ', DEFAULT_BUFFER_LINE_SIZE);
}
}
void CMemLeakHunter::endMemoryTrace()
{
newMemState.Checkpoint();
if(FALSE != diffMemState.Difference(oldMemState, newMemState))
{
CFile oDumpFile;
if(TRUE == oDumpFile.Open(sMemLeakTracePath, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
{
CFile* oldContext = afxDump.m_pFile;
afxDump.m_pFile = &oDumpFile;
diffMemState.DumpStatistics();
oDumpFile.Write("n", 1);
diffMemState.DumpAllObjectsSince();
oDumpFile.Close();
afxDump.m_pFile = oldContext;
}
else
{
// TODO: log that file cannot be created!!!
}
}
flushAllocLog();
filterTrace();
}
void CMemLeakHunter::addMemory(void* area, size_t size, LPCSTR file, int line)
{
CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);
DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
mMemChunks[nAreaAddr] = size;
if(nLogBufferLines >= LOG_BUFFER_SIZE)
{
flushAllocLog();
}
sLogBuffer[nLogBufferLines++].Format("### Memory allocation: Address 0x%08X, Size: %u, File: '%s', Line: %dn", nAreaAddr, size, file, line);
}
void CMemLeakHunter::deleteMemory(void* area, LPCSTR file, int line)
{
CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);
DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
mMemChunks.erase(nAreaAddr);
if(nLogBufferLines >= LOG_BUFFER_SIZE)
{
flushAllocLog();
}
sLogBuffer[nLogBufferLines++].Format("!!! Memory release: Address 0x%08X, File: '%s', Line: %dn", nAreaAddr, file, line);
}
void CMemLeakHunter::flushAllocLog()
{
CStdioFile oAllocFile;
if(FALSE == PathFileExists(sMemAllocTracePath))
{
oAllocFile.Open(sMemAllocTracePath, CFile::modeCreate);
oAllocFile.Close();
}
if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeReadWrite | CFile::shareDenyWrite))
{
oAllocFile.SeekToEnd();
for(size_t i=0; i<min(nLogBufferLines, LOG_BUFFER_SIZE); i++)
{
oAllocFile.WriteString(sLogBuffer[i]);
}
oAllocFile.Close();
}
else
{
// TODO: log that file cannot be accessed!!!
}
nLogBufferLines = 0;
}
void CMemLeakHunter::filterTrace()
{
CStdioFile oAllocFile;
if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeRead | CFile::shareDenyWrite))
{
CString sFilterTraceFile;
sFilterTraceFile.Format("filter_%s", sMemAllocTracePath);
CStdioFile oFilterAllocFile;
if(TRUE == oFilterAllocFile.Open(sFilterTraceFile, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
{
for (std::map<DWORD, size_t>::iterator it=mMemChunks.begin(); it!=mMemChunks.end(); it++)
{
CString addrHex;
addrHex.Format("0x%08X", it->first);
CString sLine;
while(FALSE != oAllocFile.ReadString(sLine))
{
if(sLine.Find(addrHex) > -1)
{
CString sLineWithNewline;
sLineWithNewline.Format("%sn", sLine);
oFilterAllocFile.WriteString(sLineWithNewline);
}
}
}
oFilterAllocFile.Close();
}
else
{
// TODO: log that file cannot be created!!!
}
oAllocFile.Close();
}
else
{
// TODO: log that file cannot be accessed!!!
}
}
#endif // MEMORY_LEAK_FINDER
无需自己执行此操作。
在您的主要内容中.cpp包括:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
在主函数调用的顶部:
#ifdef _DEBUG
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
对于要检测泄漏的每个文件,请将其放在顶部:
#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif // _DEBUG
然后,任何检测到的泄漏都会在应用程序退出时输出到控制台。
看这里。
- 试图在visual studio上用C++创建一个桌面应用程序
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- c++r值引用应用于函数指针
- 在C应用程序中运行C++(带有STL)函数
- 使用VerQueryValue检索应用程序的文件描述
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 使用调试/崩溃报告将应用程序部署到客户端
- C++控制台应用程序阻止退出
- 码头化的C++应用程序是否向后兼容早期的内核版本
- 将应用程序从32位移植到64位时出现问题
- 有没有任务栏API可以立即应用注册表更改
- 如何改变c++应用程序的视觉效果
- WM_CTLCOLORSTATIC从未在WIN32应用程序中触发
- PC中的程序和PHONE中的本机描述应用程序之间的数据连接
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 示例外壳应用程序显示的 V8 "segmentation fault (core dumped)"错误
- phytec phyBOARD iMX-6在从闪存而不是SD卡运行qt5 opengles应用程序时表现不佳(FPS减半
- TMap::Emplace() 在应用现有密钥时会覆盖吗?
- 为什么导入Mixed native/CLR lib.dll的本机C++应用程序没有在Mixed lib.dll中的外部变
- 如何从Windows应用程序输出到标准?