我们如何检测/避免(非托管)代码中的内存泄漏?

时间:2020-03-05 18:48:10  来源:igfitidea点击:

在非托管C / C ++代码中,检测内存泄漏的最佳实践是什么?和编码指南要避免? (好像就是这么简单;)

过去,我们使用了一些愚蠢的方法:每个内存分配调用都有一个计数器递增,释放时递减。在程序结束时,计数器值应为零。

我知道这不是一个好方法,而且有一些问题。 (例如,如果我们要释放由平台API调用分配的内存,则分配计数将与释放计数不完全匹配。当然,然后,当调用分配了内存的API调用时,我们会增加计数器。)

我期待经验,建议,以及一些可以简化此过程的工具的参考。

解决方案

回答

我自己从未使用过它,但是我的C朋友告诉我Purify。

回答

那里有各种各样的替换" malloc"库,这些库可让我们最后调用一个函数,它将告诉我们所有未释放的内存,并且在许多情况下,谁会首先分配(或者新分配)该内存。

回答

如果C / C ++代码可移植到* nix,那么没有什么比Valgrind更好的了。

回答

在Motorola手机操作系统上,我们劫持了内存分配库以观察所有内存分配。它有助于发现内存分配的许多问题。
由于预防胜于治疗,因此我建议使用静态分析工具,例如Klockwork或者PC-Lint

回答

我们是通过插值自己的syscall函数(记录调用,然后将调用传递给实函数)来计数分配和释放吗?

这是跟踪来自未编写代码的调用的唯一方法。

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

也可以使用Google LD_PRELOAD,我们会在www.itworld.com上找到一些有趣的文章来解释该技术。

回答

作为C ++开发人员,这里有一些简单的准则:

  • 仅在绝对必要时使用指针
  • 如果需要指针,请仔细检查是否有可能使用SmartPointer
  • 使用GRASP Creator模式。

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

回答

调试模式下的Microsoft VC ++会显示内存泄漏,尽管它不会显示泄漏的位置。

如果我们使用的是C ++,则始终可以避免显式地使用new:我们有vectorstringauto_ptr(在C ++ 11之前;在C ++ 11中由unique_ptr代替),unique_ptr(C ++ 11)和武器库中的" shared_ptr"(C ++ 11)。

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

回答

如果我们使用的是Visual Studio,则可能值得研究Bounds Checker。它不是免费的,但是在发现我的代码中的泄漏方面非常有用。它不仅会导致内存泄漏,还会导致GDI资源泄漏,WinAPI使用错误以及其他问题。它甚至会向我们显示泄漏内存的初始化位置,从而更容易追踪泄漏。

回答

如果我们使用的是MS VC ++,我可以从代码项目中高度推荐此免费工具:
Jochen Kalmbach的检漏仪。

我们只需将类添加到项目中,然后调用

InitAllocCheck(ACOutput_XML)
DeInitAllocCheck()

我们想要检查泄漏的代码之前和之后。

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

Rational的(现在由IBM拥有)PurifyPlus以类似的方式说明了泄漏,但是我发现检漏工具实际上更易于使用,而且其好处还不至于花费数千美元!

回答

我使用DevStudio已经有很多年了,这让我感到惊讶的是,有多少程序员不了解调试运行时库中提供的内存分析工具。以下是一些入门链接:

跟踪堆分配请求,特别是"唯一分配请求号"部分

_CrtSetDbgFlag

_CrtSetBreakAlloc

当然,如果我们不使用DevStudio,那么它将不会特别有用。

回答

至少对于MS VC ++,C运行时库具有一些我发现过去有用的功能。检查MSDN帮助中的_Crt *函数。

回答

在C ++中:使用RAII。 std :: unique_ptr,std :: shared_ptr,std :: weak_ptr等智能指针是朋友。

回答

Valgrind是Linux的不错选择。在MacOS X下,我们可以启用MallocDebug库,该库具有用于调试内存分配问题的多个选项(请参阅malloc联机帮助页,"环境"部分具有相关的详细信息)。 OS X SDK还包括一个称为MallocDebug的工具(通常安装在/ Developer / Applications / Performance Tools /中),该工具可以监视使用情况和泄漏。

回答

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

如果我们在Windows端工作,则MS调试运行时库具有一些基本的调试检测功能,并且正如另一个已经指出的那样,源代码中可以包含几个包装程序,以帮助进行泄漏检测。寻找一个可以同时使用new / delete和malloc / free的软件包,显然可以为我们提供更大的灵活性。

我对Unix方面的了解不足以提供帮助,尽管其他人也提供了帮助。

但是,不仅仅是泄漏检测,还有通过缓冲区溢出(或者溢出)来检测内存损坏的概念。我认为,这种调试功能比普通的泄漏检测更加困难。如果我们使用的是C ++对象,则这种类型的系统也会更加复杂,因为可以以多种方式删除多态类,从而在确定要删除的真正基址指针时会遇到麻烦。我知道没有一个好的"免费"系统可以对超限提供良好的保护。我们已经编写了一个系统(跨平台),发现它具有很大的挑战性。

回答

我想提供一些我过去使用过的东西:一个基本的泄漏检查器,它是源代码级别的,并且是相当自动的。
我之所以给予这个奖励有以下三个原因:

  • 我们可能会发现它很有用。
  • 尽管有点残酷,但我不要让我感到尴尬。
  • 即使它与某些win32挂钩相关联,也应易于缓解。

使用它时,我们必须注意某些事情:不要做任何需要依靠基础代码中的" new"的事情,当心关于在checkup.cpp顶部可能遗漏的情况的警告,请意识到如果打开(并解决所有问题)执行图像转储的代码,则可能会生成一个巨大的文件。

该设计旨在允许我们打开和关闭检查器,而无需重新编译包括其标头的所有内容。在要跟踪检查并重新生成一次的位置包括泄漏检查.h。之后,编译带有或者不带有LEAKCHECK #define的Leakcheck.cpp,然后重新链接以将其打开和关闭。包含unleakcheck.h将在文件中本地将其关闭。提供了两个宏:CLEARALLOCINFO()将避免在遍历分配不包含泄漏检查.h的代码时报告相同的文件和行。 ALLOCFENCE()仅在生成的报告中删除一行,而不进行任何分配。

同样,请意识到我已经有一段时间没有使用它了,我们可能需要稍微使用它。我将其放入以说明该想法。如果真的有足够的兴趣,我愿意举个例子,在此过程中更新代码,并用更好的名称替换下面URL的内容,其中包括一个语法清晰的清单。

我们可以在这里找到它:http://www.cse.ucsd.edu/~tkammeye/leakcheck.html

回答

探测:

调试CRT

避免:

Boehm GC智能指针

回答

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

我将从本文开始:
https://msdn.microsoft.com/zh-CN/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链接中还介绍了其他可以使用的技术。

回答

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

回答

对于Linux:
试用Google Perftools

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

  • 相当快(与valgrind相比:非常快)
  • 带有漂亮的结果图形显示
  • 还有其他有用的功能:cpu分析,内存使用分析...

回答

通用编码指南:

  • 资源应在分配它们的同一"层"(功能/类/库)处重新分配。
  • 如果无法做到这一点,请尝试使用一些自动解除分配(增强共享指针...)

回答

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

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

回答

我建议从软件验证中使用Memory Validator。
事实证明,该工具对帮助我追踪内存泄漏并改善我正在处理的应用程序的内存管理非常有用。

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

回答

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

回答

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

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

回答

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

回答

Mtrace似乎是Linux的标准内置工具。这些步骤是:

  • 在bash中设置环境变量MALLOC_TRACE MALLOC_TRACE = / tmp / mtrace.dat export MALLOC_TRACE;
  • 将#include <mcheck.h>添加到主源文件的顶部
  • 添加mtrace();在main和muntrace()的开头;在底部(在return语句之前)
  • 使用-g开关编译程序以获取调试信息
  • 运行你的程序
  • 使用mtrace your_prog_exe_name /tmp/mtrace.dat显示泄漏信息(我必须先使用yum install glibc_utils在我的fedora系统上安装mtrace perl脚本)

回答

内存调试工具值得一试,但是多年来,我发现可以使用两个简单的方法来防止大多数内存/资源泄漏被编码。

  • 在为要分配的资源编写获取代码之后,立即编写发布代码。通过这种方法,它更难以"遗忘",并且在某种意义上迫使人们认真考虑资源的生命周期,这些资源的生命周期是预先使用而不是暂时使用。
  • 尽可能少使用return。分配的内容应尽可能只在一个地方释放。资源获取与释放之间的条件路径应设计得尽可能简单明了。

回答

在此列表的顶部(当我阅读时)是valgrind。如果我们能够在测试系统上重现泄漏,那么Valgrind就是很好的选择。我已经成功地使用了它。

如果我们刚刚发现生产系统正在泄漏并且不知道如何在测试中重现该怎么办?在生产系统的状态下会捕获一些错误的证据,这可能足以提供问题的根源,以便我们可以重现它。

这就是蒙特卡洛采样的地方。阅读Raymond Chen的博客文章,
这个可怜的人识别内存泄漏然后检查我的实现的方式(假设Linux,仅在x86和x86-64上进行了测试)

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

回答

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