0

我正在 Visual Studio 2010 下开发一个大型 C++ 项目,并认为内部存在一些内存泄漏。我尝试了包含 crtdbg.h 的方法,但它没有多大帮助,因为我看不到泄漏发生在哪里。定义 new 有两个陷阱:首先它需要在每个 cpp 文件中完成,这不是一个真正的选项,第二它与例如 Boost 中断。使用 new(nothrow) 或任何使用 boosts "has_new_operator.h" 的东西都会打破这一点。[编辑:它无法编译,因为重新定义的“new”对于“nothrow”或“boost magic”之类的东西没有重载](除非在所有 boost 标头(包括引用 boost 的标头)之后定义“new”)

最后但同样重要的是:我有单身人士。它们是使用单例模板的子类和静态函数变量来实现的。其中之一是一个配置容器,其中一个注册设置(比存储在映射中的字符串和整数对)由于在解除分配单例实例之前调用了内存泄漏转储,因此所有这些字符串和单例本身。

有什么方法可以只显示真正的泄漏或在静态对象释放后转储?

哪些免费工具可以处理这种情况?

4

2 回答 2

2

我使用 Visual Leak Detector 获得了非常积极的结果。它小巧整洁,可以在几秒钟内构建到您的项目中(假设您有一个正在运行的调试配置):

https://vld.codeplex.com/

如果设置正确(可以使用安装程序完成),那么您只需

#include <vld.h> 

在每个模块的 .cpp 文件之一中 - 就是这样,标题将为您进行链接。你不必把它放在任何地方。该工具在内部使用 CrtDbg,因此您必须运行调试版本才能使其工作。

它在每次运行后为您提供调试器或文本输出(如果使用配置文件进行配置),即使未通过调试器运行也是如此。它不是最强大的工具,但这些通常要花一些钱;)

编辑VLD_FORCE_ENABLE:通过在包含标头之前定义,也可以在非调试配置中启用 VLD 。但结果可能会有所缓和。

编辑:我尝试过全新安装 VLD。请注意,对于 VS2013 编译器,必须使用 v2.4rc2 版本(或更高版本的 v2.3)。v2.3 版仅适用于 VS2010 编译器。

安装后,我创建了一个新项目并设置了我的包含目录和库目录以包含相应的 VLD 文件夹。之后我使用下面的代码来测试单例的 memleak 报告(注意这段代码没有意义,它只是证明了一点):

#include <iostream>
#include <string>
#include <sstream>
#include <map>

// Uncomment this, if you want VLD to work in non-debug configurations
//#define VLD_FORCE_ENABLE

#include <vld.h>

class FooSingleton {
    private:
        std::map<std::string, std::string*>
            _map;

        FooSingleton() {
        }

    public:
        static FooSingleton* getInstance(void) {
            /* THIS WOULD CAUSE LEAKS TO BE DETECTED
               SINCE THE DESTRUCTOR WILL NEVER BE CALLEd
               AND THE MAP IS NOT CLEARED.
            */
            // FooSingleton* instance = new FooSingleton;
            // return instance;

            static FooSingleton instance;
            return &instance;
        }

        void addString(const std::string& val) {
            _map.insert(std::make_pair(val, new std::string(val)));
        }

        ~FooSingleton(void) {
            auto it = _map.begin();
            auto ite = _map.end();

            for(; it != ite; ++it) {
                delete it->second;
            }
        }
};

int main(int argc, char** argv) {
    FooSingleton* fs = FooSingleton::getInstance();
    for(int i = 0; i < 100; ++i) {
        std::stringstream ss;
        ss << i << "nth string.";
        fs->addString(ss.str());
    }

    return 0;
}

使用此代码,VLD 不会报告任何泄漏,因为静态自动变量 ingetInstance()将在退出时被破坏,并且地图中的元素将被删除。尽管如此,即使是单例也必须这样做,否则将报告泄漏。但在这种情况下:

Visual Leak Detector Version 2.3 installed.
Aggregating duplicate leaks.
Outputting the report to the debugger and to D:\dev\projects\tmp\memleak\memleak\memory_leak_report.txt
No memory leaks detected. Visual Leak Detector is now exiting.

如果将 中的代码getInstance()更改为注释版本,则永远不会清除单例,并报告以下泄漏(以及其他泄漏):

---------- Block 11 at 0x008E5928: 52 bytes ----------
Leak Hash: 0x973608A9 Count: 100
  Call Stack:
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory (36): memleak.exe!std::_Allocate<std::_Tree_nod<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::alloca + 0x15 bytes
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory (187): memleak.exe!std::allocator<std::_Tree_nod<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::alloca + 0xB bytes
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (560): memleak.exe!std::_Tree_val<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,s + 0xD bytes
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (588): memleak.exe!std::_Tree_val<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,s + 0x8 bytes
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (756): memleak.exe!std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std:: + 0x17 bytes
    d:\dev\projects\tmp\memleak\memleak\main.cpp (33): memleak.exe!FooSingleton::addString + 0xA9 bytes
    d:\dev\projects\tmp\memleak\memleak\main.cpp (51): memleak.exe!main + 0x37 bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (555): memleak.exe!__tmainCRTStartup + 0x19 bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (371): memleak.exe!mainCRTStartup
    0x76BF919F (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0xE bytes
    0x7739A22B (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x84 bytes
    0x7739A201 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x5A bytes
  Data:
    C0 53 8E 00    30 67 8E 00    C0 53 8E 00    98 58 8E 00     .S..0g.. .S...X..
    30 6E 74 68    20 73 74 72    69 6E 67 2E    00 CD CD CD     0nth.str ing.....
    0C 00 00 00    0F 00 00 00    CD CD CD CD    48 56 8E 00     ........ ....HV..
    01 00 CD CD    

你可以清楚地看到Count: 100这个代码块,这是正确的。

我还在vld.ini安装目录中编辑了我的文件以启用以下设置:

AggregateDuplicates = yes
ReportTo = both

这些确保 a) 所有重复的泄漏都被压缩到一个带有泄漏计数的报告中(如上所述,否则将有 100 个条目),另一个报告文件被转储到应用程序的目录中。

因此,对于单例,只要您使用正在使用的静态自动变量方法并在析构函数中进行清理,它就可以正常工作。

编辑:此外,可以在特定代码段禁用检测。如果将上面的代码修改如下:

void addString(const std::string& val) {
    VLDDisable();
    _map.insert(std::make_pair(val, new std::string(val)));
    VLDEnable();
}

泄漏永远不会被分析和跟踪。

于 2014-11-23T09:23:17.900 回答
0

您可以从crtdebug. 除非您以相同的方式编译 boost(或任何库),否则它不会帮助您进行 boost 分配,但对于其余部分,它将向您显示分配文件和行。

这是您正确使用的方式crtdebug.h

在您stdafx.h(或任何 PCH 文件)的顶部添加以下行:

#ifdef DEBUG
    //must define both _CRTDBG_MAP_ALLOC and _CRTDBG_MAP_ALLOC_NEW
    #define _CRTDBG_MAP_ALLOC
    #define _CRTDBG_MAP_ALLOC_NEW

    #include <stdlib.h>
    #include <crtdbg.h>
    //if you won't use this macro you'll get all new as called from crtdbg.h      
    #define DEBUG_NEW   new( _CLIENT_BLOCK, __FILE__, __LINE__)
    #define new DEBUG_NEW
#endif

现在在您的mainwinmain您的程序的任何入口点的开头添加以下行:

//register memory leak check at end of execution: 
//(if you use this you won't need to use _CrtDumpMemoryLeaks at the end of your main)
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
//set report mode:
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

现在在这里我做了一个小测试:

在来自 VS10 的名为“test”的新控制台程序之后:我的 stdafx.h:

#pragma once

#ifdef _DEBUG
  #define _CRTDBG_MAP_ALLOC
  #define _CRTDBG_MAP_ALLOC_NEW
  #include <stdlib.h>
  #include <crtdbg.h>

  #define DEBUG_NEW   new( _CLIENT_BLOCK, __FILE__, __LINE__)
  #define new DEBUG_NEW
#endif

#include "targetver.h"

#include <stdio.h>
#include <tchar.h> 

我的 test.cpp 是:

#include "stdafx.h"
void CheckMemoryLeak()
{
    char *ptr=new char[100];
    int n=900;
    sprintf(ptr,"%d",n);
}

int _tmain(int argc, _TCHAR* argv[])
{
    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

    CheckMemoryLeak();

    return 0;
}

输出是:

'tests.exe': Loaded 'C:\Users\shr\Documents\Visual Studio 2010\Projects\tests\Debug\tests.exe', Symbols loaded.
'tests.exe': Loaded 'C:\Windows\SysWOW64\ntdll.dll', Cannot find or open the PDB file
'tests.exe': Loaded 'C:\Windows\SysWOW64\kernel32.dll', Cannot find or open the PDB file
'tests.exe': Loaded 'C:\Windows\SysWOW64\KernelBase.dll', Cannot find or open the PDB file
'tests.exe': Loaded 'C:\Windows\SysWOW64\msvcr100d.dll', Symbols loaded.
Detected memory leaks!
Dumping objects ->
c:\users\shr\documents\visual studio 2010\projects\tests\tests\tests.cpp(9) : {97} client block at 0x01003288, subtype 0, 100 bytes long.
 Data: <900             > 39 30 30 00 CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
The program '[1600] tests.exe: Native' has exited with code 0 (0x0).
于 2014-11-23T11:37:24.157 回答