C++ 在对象的d-tor期间检测内存泄漏

C++ 在对象的d-tor期间检测内存泄漏,c++,memory-leaks,memory-management,malloc,C++,Memory Leaks,Memory Management,Malloc,我的应用程序是基于dll的实现,在卸载期间可能会泄漏内存。我在卸载/重新加载循环期间(当宿主进程未终止时)注意到了这一点。托管进程的虚拟内存正在增加 我已经通过了代码检查,试图找到泄漏的代码,但没有找到任何 我正在寻找其他技术来检测卸载期间的内存泄漏(对象被销毁) 编辑:我正在使用win32(XP)平台 您是否有使用此类工具/程序的经验? 谢谢如果你能在一个平台上编译你的代码,你当然应该试试这个工具。读一读,看看怎么做 这里有一个简单的例子 资料来源: $ cat -n leaky.cpp

我的应用程序是基于dll的实现,在卸载期间可能会泄漏内存。我在卸载/重新加载循环期间(当宿主进程未终止时)注意到了这一点。托管进程的虚拟内存正在增加

我已经通过了代码检查,试图找到泄漏的代码,但没有找到任何

我正在寻找其他技术来检测卸载期间的内存泄漏(对象被销毁)

编辑:我正在使用win32(XP)平台

您是否有使用此类工具/程序的经验?
谢谢

如果你能在一个平台上编译你的代码,你当然应该试试这个工具。读一读,看看怎么做

这里有一个简单的例子

资料来源:

$ cat -n leaky.cpp 
     1  struct leaky
     2  {
     3      leaky()
     4          :bytes(new char[256])
     5      {
     6      }
     7  
     8      char* bytes;
     9  };
    10  
    11  int main()
    12  {
    13      leaky sieve;
    14      return sizeof sieve;
    15  }
    16  
建造:

$ make leaky
g++ -Wall -Wextra -Wshadow -pedantic -Wno-long-long -Wfloat-equal -Wcast-qual -g -I/opt/local/include -Weffc++ -Wall -I /opt/local/include -L/opt/local/lib  leaky.cpp   -o leaky
检查:

$ valgrind --leak-check=full ./leaky
==85800== Memcheck, a memory error detector
==85800== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==85800== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==85800== Command: ./leaky
==85800== 
==85800== 
==85800== HEAP SUMMARY:
==85800==     in use at exit: 2,367 bytes in 33 blocks
==85800==   total heap usage: 33 allocs, 0 frees, 2,367 bytes allocated
==85800== 
==85800== 256 bytes in 1 blocks are definitely lost in loss record 6 of 9
==85800==    at 0xB823: malloc (vg_replace_malloc.c:266)
==85800==    by 0x5768D: operator new(unsigned long) (in /usr/lib/libstdc++.6.0.9.dylib)
==85800==    by 0x576DA: operator new[](unsigned long) (in /usr/lib/libstdc++.6.0.9.dylib)
==85800==    by 0x100000EE7: leaky::leaky() (leaky.cpp:4)
==85800==    by 0x100000EB3: main (leaky.cpp:13)
==85800== 
==85800== LEAK SUMMARY:
==85800==    definitely lost: 256 bytes in 1 blocks
==85800==    indirectly lost: 0 bytes in 0 blocks
==85800==      possibly lost: 0 bytes in 0 blocks
==85800==    still reachable: 2,111 bytes in 32 blocks
==85800==         suppressed: 0 bytes in 0 blocks
==85800== Reachable blocks (those to which a pointer was found) are not shown.
==85800== To see them, rerun with: --leak-check=full --show-reachable=yes
==85800== 
==85800== For counts of detected and suppressed errors, rerun with: -v
==85800== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 1 from 1)

您没有指定要查找的平台。我是windows开发人员,因此只能推荐windows解决方案。如果这就是你正在使用的,那么有很多商业和免费的工具可用。我个人使用的很少:Purify、BoundsChecker、UMDH、LeakDiag、DebugDiag

其中,我通常更喜欢UMDH。它是免费的,是安装的一部分。我发现它实际上比大多数其他工具(包括专业工具)更可靠,资源密集度更低。它的使用非常简单,文档可以在DTW安装附带的.chm文件中找到。最后,我个人发现,与许多其他工具相比,UMDH具有非常高的信噪比

DebugDiag是另一个很好的选择。据我所知,它使用与UMDH几乎相同的API,但使用起来稍微麻烦一些,因为它是基于UI的,而不是命令提示符,所以完成事情通常需要更多的点击,但对于新手来说,我推荐它而不是UMDH

更新:

有趣的是,大多数人的偏好是在malloc/free中插入自定义挂钩,然后在new/delete操作符中添加更多自定义挂钩代码

我强烈建议您查看一下UMDH,了解它是如何工作的,即使您认为在这种特定情况下没有必要。所有内存分配的核心是windows函数HeapAlloc/HeapFree。微软预见到泄漏检测方法的需求,已经提供了我们可以在该根级别使用的钩子

与自定义分配器挂钩相比,使用UMDH还有其他优势:

  • 您将获得每个分配的完整堆栈跟踪,而不仅仅是由_文件和_行提供的__
  • 它已经有了完整的报告和统计数据的汇总,这是您必须在拦截malloc/free的基础上编写的。您可以获得每个跟踪的#个分配、每个跟踪分配的#个字节以及分配的内存缓冲区列表,以便您可以实际分析泄漏的数据类型
  • 检测其他人代码中发生的malloc/free泄漏,特别是在您控制的DLL中
  • 检测来自其他内存分配函数(如CoTaskMemAlloc或SysStringAlloc)的泄漏
  • 检测未正确释放的COM对象的泄漏
  • 当您调用第三方API返回您忘记释放的缓冲区时,检测代码中的逻辑错误
  • 您可以在任何代码库中立即使用UMDH,而无需反复添加自定义代码
  • 您接受的方法仅在调试环境中有效。UMDH可以同样有效地使用,而无需对生产系统进行任何代码更改
几乎,每当内存使用量出现上升趋势时,该工具都会告诉您它的来源。大多数情况下,如果在开发机器上可以复制,我可以在10分钟内发现泄漏(调试生产代码时需要更长的时间,因为符号文件必须匹配,有时是手动匹配)


因此,如果您通过DTW安装(顺便说一句,DTW还有其他很棒的调试功能)完全免费,那么为什么人们更喜欢使用自己的泄漏检测代码呢?

我很久以前经常做的是:

我编写了自己的
mymalloc
myrealloc
myfree
(并重载了
new
delete
,以便它们调用我的函数。)然后我编写了
malloc
realloc
宏,它们调用了
mymalloc
myrealloc
,传递它们
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuuuuuuuu。
mymalloc
所做的是:它调用了标准库的
malloc
函数,分配了一个稍大的块,并在该块中插入了
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu文件和
。它还将所有分配的块保存在一个链表中,以便以后能够遍历它们

在程序退出时,我将遍历尚未释放的块列表,并打印出导致内存泄漏的文件和行


现在,我假设会有现成的工具,你可以为你做这类事情。

除了Mike的答案(覆盖Malloc),你还可以覆盖Visual Studio中的新操作符和删除操作符

免责声明:我在2004中发现了这个代码,并把它包含在C++项目中。我不知道原始来源

下面是一个代码示例(我将其作为头文件memleak.h提供)。这是非常旧的代码,因此可能会有编译错误!但是,它确实说明了如何覆盖new和delete。它还将未释放的内存转储到文件中。如果代码中包含define\u调试,则此代码将生效

致以最良好的祝愿

#include <iostream>
#include <list>
using namespace std;

//void DumpUnfreed();
//void AddTrack(DWORD addr,  DWORD asize,  const char *fname, DWORD lnum);
//void RemoveTrack(DWORD addr);

typedef struct 
{
    DWORD   address;
    DWORD   size;
    char    file[64];
    DWORD   line;
} ALLOC_INFO;      

typedef list<ALLOC_INFO*> AllocList;   

AllocList *allocList; 

void AddTrack(DWORD addr,  DWORD asize,  const char *fname, DWORD lnum)
{
    ALLOC_INFO *info;         
    if(!allocList) 
    {
        allocList = new(AllocList);
    }         
    info = new(ALLOC_INFO);
    info->address = addr;
    strncpy(info->file, fname, 63);
    info->line = lnum;
    info->size = asize;
    allocList->insert(allocList->begin(), info);
};      

void RemoveTrack(DWORD addr)
{
    AllocList::iterator i;        
    if(!allocList)
        return;
    for(i = allocList->begin(); i != allocList->end(); i++)
    {
        if((*i)->address == addr)
        {
            allocList->remove((*i));
            break;
        }
    }
};

void DumpUnfreed()
{
    AllocList::iterator i;
    DWORD totalSize = 0;
    char buf[1024];   
    sprintf(buf, "-----------------------------------------------------------\n");
    OutputDebugString(buf);
    OutputDebugString("DSP.DLL: Detecting unfreed memory...\n");
    if(!allocList)
    {
        OutputDebugString("No memory allocations were tracked!\n");
        return;       
    }
    for(i = allocList->begin(); i != allocList->end(); i++) 
    {
        sprintf(buf, "%-50s:\t\tLINE %d,\t\tADDRESS %d\t%d unfreed\n",
            (*i)->file, (*i)->line, (*i)->address, (*i)->size);
        OutputDebugString(buf);
        totalSize += (*i)->size;
    }
    sprintf(buf, "-----------------------------------------------------------\n");
    OutputDebugString(buf);
    sprintf(buf, "DSP.DLL Total Unfreed: %d bytes\n", totalSize);
    OutputDebugString(buf);
    sprintf(buf, "-----------------------------------------------------------\n");
    OutputDebugString(buf);
};

#ifdef _DEBUG
inline void * __cdecl operator new(unsigned int size,
                                         const char *file, int line)
{
    void *ptr = (void *)malloc(size);
    AddTrack((DWORD)ptr, size, file, line);
    return(ptr);
};
inline void __cdecl operator delete(void *p)
{
    RemoveTrack((DWORD)p);
    free(p);
};
inline void * __cdecl operator new[ ] (unsigned int size, const char *file, int line)
{  
    void *ptr = (void *)malloc(size);
    AddTrack((DWORD)ptr, size, file, line);
    return(ptr);                            
};
inline void __cdecl operator delete[ ] (void *p)
{
    RemoveTrack((DWORD)p);
    free(p);
};
#endif

#ifdef _DEBUG
#define DEBUG_NEW new(__FILE__, __LINE__)
#else
#define DEBUG_NEW new
#endif
#define new DEBUG_NEW
#包括
#包括
使用名称空间std;
//void DumpUnfreed();
//void AddTrack(DWORD addr、DWORD asize、const char*fname、DWORD lnum);
//无效移除支架(DWORD addr);
类型定义结构
{
德沃德地址;
德沃德尺寸;
字符文件[64];
德沃德线;
}分配信息;
typedef列表AllocList;
阿洛