我的应用程序是基于DLL的实现,可能在卸载期间泄漏内存。我在卸载/重载循环期间注意到它(当托管进程没有被杀死时)。托管进程的虚拟内存正在增加。
我已经完成了代码检查,试图找到泄漏的代码,但没有找到任何代码。
我正在寻找其他技术来检测卸载过程中的内存泄漏(对象被破坏)。
编辑:我正在使用win32(XP)平台。
你有这方面的工具/程序经验吗? 感谢答案 0 :(得分:4)
我很久以前做的就是:
我写了自己的mymalloc
,myrealloc
和myfree
(并重载new
和delete
,以便他们调用我的函数。)然后我写了调用了malloc
和realloc
的{{1}}和mymalloc
个宏,传递了myrealloc
和__FILE__
。 __LINE__
做的是:它调用标准库的mymalloc
函数,分配稍大的块,并在该块中插入malloc
和__FILE__
。它还将所有已分配的块保留在链表中,以便以后能够遍历它们。
程序退出后,我会遍历未释放的块列表,并打印出导致内存泄漏的文件和行。
如今,我认为会有现成的工具,你可以为你做这样的事情。
答案 1 :(得分:2)
您没有指定您要查找的平台。我是Windows开发人员所以我只能推荐Windows解决方案。如果您正在使用它,那么可以使用许多商业和免费工具。我个人合作的很少:Purify,BoundsChecker,UMDH,LeakDiag,DebugDiag。
其中,我通常更喜欢UMDH。它是免费的,是Debugging Tools for Windows (DTW)安装的一部分。我发现它实际上比大多数其他更可靠,资源更少,包括专业工具。它非常简单易用,文档可以在DTW安装附带的.chm文件中找到。最后,我个人发现UMDH与许多其他工具相比具有非常高的信噪比。
DebugDiag是另一个不错的选择。据我所知,它使用与UMDH几乎相同的API,但使用起来稍微麻烦,因为它是基于UI而不是命令提示符,所以要完成任务通常需要更多点击,但对于新的人,我会推荐它UMDH。
<强>更新强>
有趣的是,多数人偏好是将自定义钩子插入malloc / free,然后将更多自定义钩子的代码添加到运算符new / delete中。
我强烈建议你看看UMDH并了解它是如何工作的,即使你在这个特定情况下没有找到必要的东西。所有内存分配的核心是Windows函数HeapAlloc / HeapFree。微软预计需要泄漏检测方法,已经提供了我们可以在根级别使用的钩子。
使用UMDH而不是自定义分配器挂钩有以下优点:
相当多,每当内存使用量呈上升趋势时,该工具会告诉您它的来源。大多数情况下,如果在开发机器上可以重现,我可以在10分钟内发现泄漏(调试生产代码需要更长的时间,因为符号文件必须匹配,有时需要手动匹配)。
因此,如果您通过DTW安装完全免费(其他btw具有其他令人敬畏的调试功能),为什么人们更愿意推出自己的泄漏检测代码?
答案 2 :(得分:1)
如果您可以在supported platform上编译代码,那么您一定要试试Valgrind的Memcheck工具。阅读The Valgrind Quick Start Guide以了解具体方法。
这是一个简单的例子......
来源:
$ 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)
答案 3 :(得分: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:\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