125

In unmanaged C/C++ code, what are the best practices to detect memory leaks? And coding guidelines to avoid? (As if it's that simple ;)

We have used a bit of a silly way in the past: having a counter increment for every memory allocation call and decrement while freeing. At the end of the program, the counter value should be zero.

I know this is not a great way and there are a few catches. (For instance, if you are freeing memory which was allocated by a platform API call, your allocation count will not exactly match your freeing count. Of course, then we incremented the counter when calling API calls that allocated memory.)

I am expecting your experiences, suggestions and maybe some references to tools which simplify this.

4

29 回答 29

79

If your C/C++ code is portable to *nix, few things are better than Valgrind.

于 2008-09-05T12:22:55.193 回答
65

如果您使用的是 Visual Studio,Microsoft 提供了一些有用的功能来检测和调试内存泄漏。

我将从这篇文章开始: https ://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.140).aspx

这是这些文章的快速摘要。首先,包括这些标题:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

然后你需要在程序退出时调用它:

_CrtDumpMemoryLeaks();

或者,如果您的程序不是每次都在同一个地方退出,您可以在程序开始时调用它:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

现在,当程序退出时,所有未释放的分配将与分配它们的文件和分配发生一起打印在输出窗口中。

此策略适用于大多数程序。但是,在某些情况下,它变得困难或不可能。使用在启动时进行一些初始化的第三方库可能会导致其他对象出现在内存转储中,并且会使跟踪您的泄漏变得困难。此外,如果您的任何类具有与任何内存分配例程(例如 malloc )同名的成员,则 CRT 调试宏将导致问题。

上面引用的 MSDN 链接中还解释了其他技术,这些技术也可以使用。

于 2008-09-08T16:30:57.587 回答
36

在 C++ 中:使用 RAII。std::unique_ptr像, std::shared_ptr,之类的智能指针std::weak_ptr是您的朋友。

于 2008-09-05T14:38:15.667 回答
28

作为 C++ 开发人员,这里有一些简单的指导方针:

  1. 仅在绝对必要时使用指针
  2. 如果您需要指针,请仔细检查是否可以使用SmartPointer
  3. 使用 GRASP Creator模式。

至于内存泄漏的检测,我个人一直使用Visual Leak Detector,发现它非常有用。

于 2008-09-05T12:26:42.737 回答
16

我已经使用 DevStudio 很多年了,我总是惊讶于有多少程序员不了解调试运行时库中可用的内存分析工具。以下是一些开始使用的链接:

跟踪堆分配请求- 特别是关于唯一分配请求编号的部分

_CrtSetDbgFlag

_CrtSetBreakAlloc

当然,如果您不使用 DevStudio,那么这不会特别有用。

于 2008-09-05T12:55:54.227 回答
10

我很惊讶没有人提到Windows 操作系统的DebugDiag
它适用于发布版本,甚至适用于客户站点。
(您只需要保留您的发布版本 PDB,并将 DebugDiag 配置为使用 Microsoft 公共符号服务器)

于 2008-10-12T11:37:29.043 回答
7

Visual Leak Detector 是一个非常好的工具,尽管它不支持对 VC9 运行时的调用(例如 MSVCR90D.DLL)。

于 2008-12-22T23:19:33.600 回答
7

调试模式下的 Microsoft VC++ 显示内存泄漏,尽管它没有显示您的泄漏在哪里。

如果您使用 C++,则始终可以避免显式使用 new:您的武器库中有vector, string, auto_ptr(pre C++11; 在 C++11 中替换为unique_ptr), unique_ptr(C++11) 和shared_ptr(C++11)。

当 new 不可避免时,尝试将其隐藏在构造函数中(并在析构函数中隐藏 delete);同样适用于 3rd 方 API。

于 2008-09-05T12:28:12.857 回答
4

如果您使用的是 MS VC++,我强烈推荐这个来自 codeproject 的免费工具:Jochen Kalmbach 的 leakfinder

您只需将类添加到您的项目中,然后调用

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

在要检查泄漏的代码之前和之后。

构建并运行代码后,Jochen 提供了一个简洁的 GUI 工具,您可以在其中加载生成的 .xmlleaks 文件,并浏览生成每个泄漏的调用堆栈以查找有问题的代码行。

Rational(现在由 IBM 拥有)的 PurifyPlus 以类似的方式说明了泄漏,但我发现泄漏查找工具实际上更易于使用,而且它的成本不超过几千美元!

于 2008-09-05T12:54:00.347 回答
4

There are various replacement "malloc" libraries out there that will allow you to call a function at the end and it will tell you about all the unfreed memory, and in many cases, who malloced (or new'ed) it in the first place.

于 2008-09-05T12:22:51.233 回答
3

如果您使用的是 Visual Studio,可能值得一看Bounds Checker。它不是免费的,但它对查找我的代码中的漏洞非常有帮助。它不仅会发生内存泄漏,还会发生 GDI 资源泄漏、WinAPI 使用错误和其他问题。它甚至会向您显示泄漏内存的初始化位置,从而更容易追踪泄漏。

于 2008-09-05T12:50:16.580 回答
3

Never used it myself, but my C friends tell me Purify.

于 2008-09-05T12:22:31.000 回答
2

I'd like to offer something I've used at times in the past: a rudimentary leak checker which is source level and fairly automatic. I'm giving this away for three reasons:

  1. You might find it useful.

  2. Though it's a bit krufty, I don't let that embarass me.

  3. Even though it's tied to some win32 hooks, that should be easy to alleviate.

There are things of which you must be careful when using it: don't do anything that needs to lean on new in the underlying code, beware of the warnings about cases it might miss at the top of leakcheck.cpp, realize that if you turn on (and fix any issues with) the code that does image dumps, you may generate a huge file.

The design is meant to allow you to turn the checker on and off without recompiling everything that includes its header. Include leakcheck.h where you want to track checking and rebuild once. Thereafter, compile leakcheck.cpp with or without LEAKCHECK #define'd and then relink to turn it on and off. Including unleakcheck.h will turn it off locally in a file. Two macros are provided: CLEARALLOCINFO() will avoid reporting the same file and line inappropriately when you traverse allocating code that didn't include leakcheck.h. ALLOCFENCE() just drops a line in the generated report without doing any allocation.

Again, please realize that I haven't used this in a while and you may have to work with it a bit. I'm dropping it in to illustrate the idea. If there turns out to be sufficient interest, I'd be willing to work up an example, updating the code in the process, and replace the contents of the following URL with something nicer that includes a decently syntax-colored listing.

You can find it here: http://www.cse.ucsd.edu/~tkammeye/leakcheck.html

于 2008-09-05T17:31:04.143 回答
2

我建议使用软件验证中的内存验证器。事实证明,这个工具对帮助我追踪内存泄漏和改进我正在处理的应用程序的内存管理非常有用。

一个非常完整和快速的工具。

于 2008-09-21T12:59:27.257 回答
2

对于 Linux:试试Google Perftools

有很多工具可以进行类似的分配/释放计数,Goolge Perftools 的优点:

  • 相当快(与 valgrind 相比:非常快)
  • 带有漂亮的结果图形显示
  • 具有其他有用的功能:cpu-profiling、memory-usage profiling...
于 2008-09-16T08:15:39.003 回答
2

防止泄漏的最佳方法是尽量减少 malloc 使用的程序结构。这不仅从编程的角度来看是好的,而且还提高了性能和可维护性。我不是在谈论使用其他东西来代替 malloc,而是在重用对象和对所有被传递的对象保持非常明确的选项卡而不是像在垃圾收集器的语言中经常习惯的那样随意分配像Java。

例如,我处理的一个程序有一堆代表图像数据的框架对象。每个框架对象都有子数据,框架的析构函数会释放这些子数据。该程序保留所有已分配帧的列表,当它需要一个新的帧时,检查未使用的帧对象的列表,看看它是否可以重新使用现有的帧对象而不是分配一个新的帧对象。关机时,它只是遍历列表,释放所有内容。

于 2008-09-16T08:22:26.273 回答
2

您是否通过插入自己的系统调用函数来计算分配和释放,这些函数记录调用然后将调用传递给真正的函数?

这是您可以跟踪源自您未编写的代码的调用的唯一方法。

查看 ld.so 的手册页。或 ld.so.1 在某些系统上。

也做谷歌 LD_PRELOAD,你会在 www.itworld.com 上找到一些解释该技术的有趣文章。

于 2008-09-05T12:24:02.867 回答
2

我认为这个问题没有简单的答案。您如何真正接近此解决方案取决于您的要求。您需要跨平台解决方案吗?您使用的是 new/delete 还是 malloc/free(或两者都使用)?您真的只是在寻找“泄漏”还是想要更好的保护,例如检测缓冲区溢出(或欠载)?

如果您在 Windows 端工作,MS 调试运行时库具有一些基本的调试检测功能,正如另一个已经指出的那样,您的源代码中可以包含几个包装器来帮助进行泄漏检测。找到一个可以同时使用 new/delete 和 malloc/free 的包显然会给你更多的灵活性。

我对 unix 方面的了解不够,无法提供帮助,尽管同样,其他人也有。

但除了泄漏检测之外,还有通过缓冲区溢出(或欠载)检测内存损坏的概念。我认为这种类型的调试功能比普通的泄漏检测更难。如果您使用 C++ 对象,这种类型的系统也会更加复杂,因为可以以不同的方式删除多态类,从而导致在确定要删除的真正基指针时变得棘手。我知道没有一个好的“免费”系统可以很好地保护溢出。我们编写了一个系统(跨平台),发现它非常具有挑战性。

于 2008-09-05T14:49:18.603 回答
1

Paul Nettle 的 mmgr是我长期以来最喜欢的工具。您在源文件中包含 mmgr.h,定义 TEST_MEMORY,它会提供一个文本文件,其中包含在您的应用程序运行期间发生的内存问题。

于 2008-09-13T23:37:31.013 回答
1

至少对于 MS VC++,C 运行时库有几个我在过去发现有用的函数。查看 MSDN 帮助以了解这些_Crt*功能。

于 2008-09-05T13:12:12.127 回答
1

一般编码指南:

  • 资源应该在分配它们的同一“层”(函数/类/库)中释放。
  • 如果这是不可能的,请尝试使用一些自动释放(提升共享指针......)
于 2008-09-16T08:17:48.437 回答
1

Working on Motorola cell phones operating system, we hijacked memory allocation library to observe all memory allocations. It helped to find a lot of problems with memory allocations. Since prevention is better then curing, I would recommend to use static analysis tool like Klockwork or PC-Lint

于 2008-09-05T12:23:44.007 回答
1

在这个列表的顶部(当我读到它时)是 valgrind。如果您能够在测试系统上重现泄漏,则 Valgrind 非常出色。我使用它取得了巨大的成功。

如果您刚刚注意到生产系统现在正在泄漏并且您不知道如何在测试中重现它怎么办?在该生产系统的状态中捕获了一些错误的证据,这可能足以提供有关问题所在的洞察力,以便您可以重现它。

这就是蒙特卡洛抽样发挥作用的地方。阅读 Raymond Chen 的博客文章“穷人识别内存泄漏的方法”,然后查看我的实现(假设 Linux,仅在 x86 和 x86-64 上测试)

http://github.com/tialaramex/leakdice/tree/master

于 2009-06-20T08:59:46.997 回答
1

内存调试工具物超所值,但多年来我发现可以使用两个简单的想法来防止大多数内存/资源泄漏首先被编码。

  1. 为要分配的资源编写获取代码后立即编写发布代码。使用这种方法,它更难“忘记”,并且在某种意义上迫使人们认真考虑预先使用的资源的生命周期,而不是作为旁白。

  2. 尽可能谨慎地使用 return。如果可能,分配的内容应该只在一个地方释放。获取资源和释放资源之间的条件路径应该设计得尽可能简单明了。

于 2009-06-16T03:58:20.683 回答
0

Valgrind 是 Linux 的一个不错的选择。在 MacOS X 下,您可以启用 MallocDebug 库,该库有几个用于调试内存分配问题的选项(参见 malloc 手册页,“环境”部分有相关详细信息)。OS X SDK 还包括一个名为 MallocDebug 的工具(通常安装在 /Developer/Applications/Performance Tools/ 中),它可以帮助您监控使用情况和泄漏。

于 2008-09-05T14:42:50.850 回答
0

探测:

调试 CRT

避免:

智能指针,boehm GC

于 2008-09-06T09:35:46.930 回答
0

一个不错的 malloc、calloc 和 reallloc 替代品是 rmdebug,它使用起来非常简单。它比 valgrind 快得多,因此您可以广泛地测试您的代码。当然它也有一些缺点,一旦你发现了泄漏,你可能仍然需要使用 valgrind 来查找泄漏出现的位置,并且你只能测试你直接执行的 malloc。如果一个库因为使用错误而泄漏,rmdebug 将找不到它。

http://www.hexco.de/rmdebug/

于 2008-10-12T12:17:38.620 回答
0

大多数内存分析器将我的大型复杂 Windows 应用程序减慢到结果无用的地步。有一种工具可以很好地发现我的应用程序中的泄漏:UMDH - http://msdn.microsoft.com/en-us/library/ff560206%28VS.85%29.aspx

于 2010-12-02T22:48:41.283 回答
-1

Mtrace似乎是 linux 的标准内置程序。步骤是:


  1. 在 bash MALLOC_TRACE=/tmp/mtrace.dat中设置环境变量MALLOC_TRACE
    export MALLOC_TRACE;
  2. #include <mcheck.h>添加到主源文件的顶部
  3. 添加mtrace(); 在 main 和muntrace() 的开头;在底部(return 语句之前)
  4. 使用 -g 开关编译程序以获取调试信息
  5. 运行你的程序

  6. 使用mtrace your_prog_exe_name /tmp/mtrace.dat显示泄漏信息
    (我必须先在我的 Fedora 系统上使用yum install glibc_utils安装 mtrace perl 脚本  )
于 2009-06-16T03:08:01.560 回答