=============================================================
标题:检测由new/delete使用不当引起的内存泄露
备注:wince
日期:2011.5.4
姓名:朱铭雷
=============================================================
以前在网上看到过wince下可用的检测new/delete的小程序,不过好像在mfc下使用还有点问题。但实在是不记得文章的名字了,也没有找到原文地址。先前开会时,同事也提到了这个事情,一定要严防memory leak。所以凭着一点记忆和对new/delete的一点简单理解,勉强写了这么一个简陋的检测程序。不过这份简陋的程序还是起了作用的,它比较擅长的就是当项目越来越大时,new出来的内存忘记delete,或者是由于没有对某些跳转返回考虑周全而使程序在某些情况下越过了delete,亦或是某些时候这个内存需要同事来释放而双方又没有去沟通时等等产生的内存泄露。正是这份极为简陋的程序,检测出了我们当前项目中几处极为严重的内存泄露,特别是其中一处,几乎是每次执行该功能就会泄露将近1M的宝贵内存。由于这份程序基本上是凭着记忆“参考”网上那位无名大侠的(记得是codeproject上看到的),所以在这里表示感谢。也希望看到俺这篇日记的朋友,能够给点意见,帮助修改错误,填补不足,抑或是这段程序从根本上就是不对的话,重构一个。我实在是实力和时间都不足,希望搞wince的朋友们群策群力,多共享些好用的工具,帮助大家更好的工作。
下面是这份程序检测到内存泄露,将信息写到了vs的输出窗口:
双击所输出的内存泄露信息,将会跳转到引起内存泄露的相应文件的相应代码行:new_delete_memleak_v11Dlg.cpp文件的第68行。
下面是相关的3个文件的源代码:
/******************************************************************************** 文件名 : Ce_MemLeak_Debug.h 相关文件 : Ce_MemLeak_Debug.cpp 文件实现功能 : 检测由new,delete使用不当造成的内存泄露, 可直接定位到导致内存泄露的代码行 作者 : zhuyf 版本 : 1.1 备注 : (1) 存在一定的问题,但不影响目前的检测功能 (2) “placement new”合乎常理的写法应该是其内部有一个循环,在循环中尝试 分配内存,如果无法找到足够内存,则调用“new-handler”。但本程序并不 是打算替换global new/delete,也不打算替换class专属的new/delete,只 是为了协助检测new/delete产生的内存泄露,所以不要试图将本程序中重载的 new/delete代替原有的new/delete来使用。 (3) 此版本针对MFC 日期 : 2010-04-26 修改记录 : 增加“placement delete”,避免“分配内存成功,而对象构造函数中抛出 异常,这时运行期系统在试图寻找placement delete时失败,而导致无法 恢复原态,从而导致非常细微难以查找的内存泄露”情况。 修改时间 : 2011-05-04 ********************************************************************************/ #pragma once #if defined(_DEBUG) && defined(_WIN32_WCE) class GarbageCollector { public: GarbageCollector(); ~GarbageCollector(); }; void* operator new(size_t size, char* pszFileName, int iLineNum); void operator delete(void *pMemory, char* pszFileName, int iLineNum); void operator delete(void *pMemory); #undef DEBUG_NEW #define DEBUG_NEW ::new (__FILE__, __LINE__) extern GarbageCollector g_GC; #endif // if defined(_DEBUG) && defined(_WIN32_WCE)
/******************************************************************************** 文件名 : Ce_MemLeak_Debug.cpp 相关文件 : Ce_MemLeak_Debug.h 文件实现功能 : 检测由new,delete使用不当造成的内存泄露, 可直接定位到导致内存泄露的代码行 作者 : zhuyf 版本 : 1.1 备注 : (1) 存在一定的问题,但不影响目前的检测功能 (2) “placement new”合乎常理的写法应该是其内部有一个循环,在循环中尝试 分配内存,如果无法找到足够内存,则调用“new-handler”。但本程序并不 是打算替换global new/delete,也不打算替换class专属的new/delete,只 是为了协助检测new/delete产生的内存泄露,所以不要试图将本程序中重载的 new/delete代替原有的new/delete来使用。 (3) 此版本针对MFC 日期 : 2010-04-26 修改记录 : 增加“placement delete”,避免“分配内存成功,而对象构造函数中抛出 异常,这时运行期系统在试图寻找placement delete时失败,而导致无法 恢复原态,从而导致非常细微难以查找的内存泄露”情况。 修改时间 : 2011-05-04 ********************************************************************************/ #include "stdafx.h" #include "Ce_MemLeak_Debug.h" #if defined(_DEBUG) && defined(_WIN32_WCE) typedef struct _FILENAME { char* szFileName; _FILENAME* pNextFile; }FILENAME, *PFILENAME; typedef struct _MEMINFO { PFILENAME pFileName; int iLineNum; UINT nMemLen; void* pMemAddr; _MEMINFO* pNextMemInfo; }MEMINFO, *PMEMINFO; GarbageCollector g_GC; PMEMINFO g_pMemInfoRoot = 0; PFILENAME g_pFileNameRoot = 0; void* operator new(size_t size, char* pszFileName, int iLineNum) { void* pMem = malloc(size); if (pMem) { PMEMINFO pMemInfo = (PMEMINFO)malloc(sizeof(MEMINFO)); pMemInfo->iLineNum = iLineNum; pMemInfo->nMemLen = size; pMemInfo->pMemAddr = pMem; pMemInfo->pNextMemInfo = 0; PFILENAME pTmpFileName = NULL; for (pTmpFileName = g_pFileNameRoot; pTmpFileName && strcmp(pszFileName, pTmpFileName->szFileName); pTmpFileName = pTmpFileName->pNextFile); if (!pTmpFileName) { char* pszName = (char*)malloc((strlen(pszFileName) + 1) * sizeof(char)); strcpy(pszName, pszFileName); PFILENAME pFileName = (PFILENAME)malloc(sizeof (FILENAME)); pFileName->szFileName = pszName; pFileName->pNextFile = 0; if (!g_pFileNameRoot) { g_pFileNameRoot = pFileName; } else { for (pTmpFileName = g_pFileNameRoot; pTmpFileName->pNextFile; pTmpFileName = pTmpFileName->pNextFile); pTmpFileName->pNextFile = pFileName; } pTmpFileName = pFileName; } pMemInfo->pFileName = pTmpFileName; if (!g_pMemInfoRoot) { g_pMemInfoRoot = pMemInfo; } else { PMEMINFO pTmpMemInfo = NULL; for (pTmpMemInfo = g_pMemInfoRoot; pTmpMemInfo->pNextMemInfo; pTmpMemInfo = pTmpMemInfo->pNextMemInfo); pTmpMemInfo->pNextMemInfo = pMemInfo; } } return pMem; } void operator delete(void *pMemory, char* pszFileName, int iLineNum) { if (pMemory) { PMEMINFO pTmpMemInfo = 0; if (g_pMemInfoRoot) { if (pMemory == g_pMemInfoRoot->pMemAddr) { pTmpMemInfo = g_pMemInfoRoot; g_pMemInfoRoot = g_pMemInfoRoot->pNextMemInfo; free(pTmpMemInfo); } else { for (pTmpMemInfo = g_pMemInfoRoot; pTmpMemInfo->pNextMemInfo && (pTmpMemInfo->pNextMemInfo->pMemAddr != pMemory); pTmpMemInfo = pTmpMemInfo->pNextMemInfo); if (pTmpMemInfo->pNextMemInfo) { PMEMINFO pTmp2MemInfo; pTmp2MemInfo = pTmpMemInfo->pNextMemInfo; pTmpMemInfo->pNextMemInfo = pTmp2MemInfo->pNextMemInfo; free(pTmp2MemInfo); } else { // NKDbgPrintfW(_T("%s(%i) : Warning : deletes memory pointer not allocated with new!/n"), _T(__FILE__), __LINE__); } } free (pMemory); } } } void operator delete(void *pMemory) { if (pMemory) { PMEMINFO pTmpMemInfo = 0; if (g_pMemInfoRoot) { if (pMemory == g_pMemInfoRoot->pMemAddr) { pTmpMemInfo = g_pMemInfoRoot; g_pMemInfoRoot = g_pMemInfoRoot->pNextMemInfo; free(pTmpMemInfo); } else { for (pTmpMemInfo = g_pMemInfoRoot; pTmpMemInfo->pNextMemInfo && (pTmpMemInfo->pNextMemInfo->pMemAddr != pMemory); pTmpMemInfo = pTmpMemInfo->pNextMemInfo); if (pTmpMemInfo->pNextMemInfo) { PMEMINFO pTmp2MemInfo; pTmp2MemInfo = pTmpMemInfo->pNextMemInfo; pTmpMemInfo->pNextMemInfo = pTmp2MemInfo->pNextMemInfo; free(pTmp2MemInfo); } else { // NKDbgPrintfW(_T("%s(%i) : Warning : deletes memory pointer not allocated with new!/n"), _T(__FILE__), __LINE__); } } free (pMemory); } } } GarbageCollector::GarbageCollector() { } GarbageCollector::~GarbageCollector () { USES_CONVERSION; if (!g_pMemInfoRoot) { NKDbgPrintfW (_T("------------------------------------------------------------------------------------/n")); NKDbgPrintfW(_T("-----------------------------No memory leaks detected!------------------------------/n")); NKDbgPrintfW (_T("------------------------------------------------------------------------------------/n")); } else { PMEMINFO pTmpMemInfo = NULL; NKDbgPrintfW (_T("------------------------------------------------------------------------------------/n")); NKDbgPrintfW (_T("-------------------------------Detected memory leaks!-------------------------------/n")); NKDbgPrintfW (_T("------------------------------------------------------------------------------------/n")); for (pTmpMemInfo = g_pMemInfoRoot; pTmpMemInfo; pTmpMemInfo = pTmpMemInfo->pNextMemInfo) { NKDbgPrintfW(_T("%s(%i) : normal block at 0x%08X, %i bytes long/n"), A2W(pTmpMemInfo->pFileName->szFileName), pTmpMemInfo->iLineNum, pTmpMemInfo->pMemAddr, pTmpMemInfo->nMemLen); } NKDbgPrintfW (_T("------------------------------------------------------------------------------------/n")); } PFILENAME pTmpFileName = g_pFileNameRoot; for (; pTmpFileName; ) { g_pFileNameRoot = pTmpFileName->pNextFile; free(pTmpFileName->szFileName); free(pTmpFileName); pTmpFileName = g_pFileNameRoot; } } #endif // if defined(_DEBUG) && defined(_WIN32_WCE)
/******************************************************************************** 文件名 : Ce_MFC_debug.h 相关文件 : Ce_MemLeak_Debug.h Ce_MemLeak_Debug.cpp 文件实现功能 : 配合Ce_MemLeak_Debug.h Ce_MemLeak_Debug.cpp使用 作者 : zhuyf 版本 : 1.0 备注 : 无 日期 : 2010-04-26 修改记录 : 无 ********************************************************************************/ #pragma once #ifdef _DEBUG #define new DEBUG_NEW #endif
使用方法应该是非常简单的,可以在stdafx.h头文件中加入#include "Ce_MemLeak_Debug.h",在需要的.cpp文件中加入#include "Ce_MFC_debug.h"。之后充分!运行程序。在程序退出时,就会将“内存泄露信息”写到Debug输出窗口。