定位内存泄漏问题

4

我目前正在使用带有CrtDbg的Visual Studio 2012调试代码,移除或至少定位内存泄漏。

问题是,只要分配号不改变,跟踪分配就很容易。当分配号经常改变(或者不是真正确定的)时,我该如何定位泄漏的分配点?我最起码能不能说出是哪个模块分配了内存?

在应用程序关闭时,我有以下几行代码:

Detected memory leaks!
Dumping objects ->
{2789444} normal block at 0x0000000006103CB0, 32 bytes long.
 Data: < q f            > B8 71 E4 66 00 00 00 00 00 00 00 00 00 00 00 00 
{1269709} normal block at 0x000000000A50C6A0, 1008 bytes long.
 Data: <        )       > 01 00 00 00 0B 00 00 00 29 00 00 00 CD CD CD CD 
...
{2194} normal block at 0x0000000000278060, 16 bytes long.
 Data: <                > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
Object dump complete.

最后的分配号码2194与静态初始化器有关,可以重现。但其他号码会变化。
我能否使用地址来定位它?或者有更简单的解决方案吗?
谢谢帮助。

这段代码能否使用像valgrind这样的分析工具进行性能分析? - roelofs
啊,好吧,那个想法泡汤了 :( - roelofs
已经考虑过了...Valgrind 在 Linux 上看起来很酷... - roalter
如果这不是一个原生应用的话,那或许会有其他选择 ;) - roelofs
@roelofs - 我也遇到了同样的问题 - 请参见https://dev59.com/t4Lba4cB1Zd3GeqPaSEG - Ed Heal
显示剩余2条评论
4个回答

1
我建议您使用Visual Leak Detector。我将为您提供所有必要的细节,以便您可以跟踪泄漏并轻松安装。 请参见这里

已经尝试过了。但问题在于生成这些分配的代码可能是第三方而没有源代码。或者我可以在不包含它在适当文件中的情况下使用它吗?我认为不行。 - roalter
如果是没有调试信息的第三方库,无论你做什么都没办法。你能得到的最好结果就是将泄漏定位到特定的库。 - roelofs
这就是我想做的。但是我如何(轻松地?)将泄漏与特定库匹配起来呢?有什么建议吗? - roalter
性能分析器(免责声明:我不熟悉VLD)应该至少告诉您泄漏源自哪个模块/库/对象文件。如果您有调试信息,那么您可以查看是否是您可能正在使用(或使用不正确的)函数。 - roelofs

1
尝试使用Debug Diagnostic Tool v2.0,它是Windows上非常好的内存检测工具,来自Microsoft且免费。
如果第三方库泄漏内存,该工具可以定位库,只是没有调用堆栈信息。
要通过此调试器启动exe,请转到菜单“工具” ->“预附加配置”,为您的exe启用预附加调试器。

嗯,我会尝试一下。听起来很有前途。 - roalter
但只能附加到正在运行的进程上。我会看看结果是什么... :) - roalter
它并没有真正地工作。我该如何直接从DebugDiag启动我的应用程序? - roalter
不是很好用。得到了太多的误报。对于.NET应用程序不错,但对于本地代码不适用 :( - roalter

0

下载visual-leak-detector并开始以下操作:

创建以下目录结构:

Visual Leak Detector
    include
        vld.h
        vld_def.h
    lib
        Win32
            vld.lib
        Win64
            vld.lib
    bin
        Win32
            vld_x86.dll
        Win64
            vld_x64.dll

在你的主函数上方添加以下内容:

#ifdef _DEBUG_MEM
#include <vld.h>
#endif

在您的项目设置中添加以下内容:

_DEBUG_MEM in the preprocessor-definitions
Visual Leak Detector\include in the include-path
Visual Leak Detector\lib\Win<xx> in the library-path
Visual Leak Detector\bin\Win<xx> in the executable-path

我发现它可能会产生误报。 - Ed Heal
没有完美的分析器,这是我的经验。在复杂的代码中,通常需要进行有根据的猜测。 - roelofs
为什么我需要另一个LeakDetector?WinDbg已经包含了自己的跟踪例程,使用这些功能也可以实现跟踪。问题不在于我的代码,而是来自第三方库的泄漏,这些库没有调试信息或泄漏跟踪。 - roalter

0

您可以使用以下技巧:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
class MemChecker 
{
   friend class foo;
   struct foo 
   {
      HANDLE hLogFile;
      _CrtMemState _ms; 

      foo() 
      {
         hLogFile = CreateFile(TEXT("memory_leaks.txt"), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

         _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); // enable file output
         _CrtSetReportFile( _CRT_WARN, hLogFile ); // set file to stdout
         _CrtMemCheckpoint(&_ms); // now forget about objects created before

         // breaks on N-th memory allocation
         // look for this number in report file (in curved brackets)
         //_CrtSetBreakAlloc(1518);
      }
      ~foo() 
      { 
         _CrtMemDumpAllObjectsSince(&_ms); // dump leaks
         CloseHandle(hLogFile);
      }
   };
   static foo obj;
};
MemChecker::foo MemChecker::obj;

通过这个声明,每次运行程序时都会检测并以适当的方式报告内存泄漏。
此外,您可以将断点设置到特定的内存泄漏处(请阅读代码中的注释)。如何做到这一点:您运行应用程序,查看内存泄漏的数量,然后设置_CrtSetBreakAlloc(2789444 *),下次运行应用程序时,它会在分配内存(泄漏)的位置中断。
您可以更仔细地阅读有关标志_CRTDBG_MODE_FILE、_CRTDBG_MODE_DEBUG的信息,以指定调试消息输出的位置。
*从您的示例中,{2789444}是位于0x0000000006103CB0的普通块,长度为32字节。

不适用于第三方库,特别是在使用Qt时... (参见http://qt-project.org/forums/viewthread/837和https://bugreports.qt-project.org/browse/QTBUG-40575) - roalter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接