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



我的应用程序是基于dll的实现,可能在卸载期间泄漏内存。我注意到它在卸载/重新加载周期(当托管进程没有被杀死)。主机进程的虚拟内存正在增加。

我已经进行了代码检查,试图找到泄漏的代码,但没有找到。

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

EDIT:我使用的是win32 (XP)平台。

你有使用这些工具/程序的经验吗?谢谢

很久以前我经常这样做:

我自己写了mymalloc, myreallocmyfree(并重载了newdelete,以便它们调用我的函数)。然后我写了mallocrealloc宏,它们调用mymallocmyrealloc,传递给它们__FILE____LINE__mymalloc所做的是这样的:它调用标准库的malloc函数,分配一个稍大的块,并在该块中插入__FILE____LINE__。它还将所有分配的块保存在一个链表中,以便以后能够遍历它们。

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

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

您没有指定要查找的平台。我是一个windows开发者,所以我只能推荐windows解决方案。如果这就是你的工作,有许多商业和免费的工具可用。我个人使用过的几个:Purify, BoundsChecker, UMDH, LeakDiag, DebugDiag。

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

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

更新:

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

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

使用UMDH优于自定义分配器钩子的其他优点:

  • 您可以获得每个分配的完整堆栈跟踪,而不仅仅是__FILE__和__LINE__
  • 提供的内容
  • 它已经有完整的报告和统计汇总,这是你必须在拦截malloc/free的基础上编写的东西。您可以获得每个跟踪分配的#个字节,每个跟踪分配的#个字节以及分配的内存缓冲区列表,以便您可以实际分析泄漏的数据类型。
  • 检测发生在其他人代码中的malloc/free泄漏,而不是您控制下的DLL
  • 检测其他内存分配函数(如CoTaskMemAlloc或SysStringAlloc)的泄漏
  • 检测未正确释放的COM对象的泄漏
  • 当你调用第三方API返回一个你忘记释放的缓冲区时,检测代码中的逻辑错误。
  • 您可以立即使用任何代码库的UMDH,而无需一遍又一遍地添加自定义代码。
  • 您接受的方法只能在调试环境中工作。UMDH可以同样有效地使用,而无需对生产系统进行任何代码更改。

基本上,任何时候内存使用有上升趋势,这个工具都会告诉你它来自哪里。大多数情况下,如果在开发机器上可以重现,我可以在10分钟内找到泄漏(调试生产代码时需要更长的时间,因为必须匹配符号文件,有时需要手动)。

那么,如果您可以通过DTW安装(btw有其他很棒的调试功能)获得所有这些完全免费的功能,为什么人们更喜欢使用自己的泄漏检测代码呢?

如果您可以在受支持的平台上编译代码,那么您当然应该尝试一下Valgrind的Memcheck工具。阅读Valgrind快速入门指南,了解如何。

这里有一个简单的例子…

源:

$ 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)

除了Mike的答案(重写Malloc)之外,您还可以在Visual Studio中重写new和delete操作符。

免责声明:我早在2004年就在网上找到了这段代码,并将其包含在一个c++项目中。我不知道原始来源

下面是一个代码示例(我将其作为头文件memleak.h包含在内)。这是相当旧的代码,所以它可能有错误编译!不过,它确实说明了如何重写new和delete。它还将未释放的内存转储到文件中。如果在代码中包含了define _DEBUG,这段代码就会生效。

致以最诚挚的问候

#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:ttLINE %d,ttADDRESS %dt%d unfreedn",
            (*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 bytesn", 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

相关内容

  • 没有找到相关文章

最新更新