本文档描述如何检查 C++ 程序的堆使用情况。这个工具可以用于自动检测内存泄漏。
链接堆检查器
你可以对任何链接了 tcmalloc
库的程序进行堆检查。使用堆检查器不需要重新编译。
为了捕获所有堆泄漏,tcmalloc
必须最后链接到您的可执行文件中。
堆检查器可能会错误地描述链接行后面列出的库中的一些内存访问。例如,当这些库没有泄漏内存时,它可能报告它们为泄漏内存(更多细节请参见源代码)。
即使你不希望对你的程序进行堆检查,链接到 tcmalloc
也是安全的。
只要不使用任何堆检查器特性,程序就不会运行得更慢。
你可以在不是自己编译的应用程序上运行堆检查器,方法是使用LD_PRELOAD
:
$ LD_PRELOAD="/usr/lib/libtcmalloc.so" HEAPCHECK=normal
我们不一定推荐这种使用模式。
打开堆检查
对于给定的可执行文件运行,有两种方法可以打开堆检查:
-
对于整个程序堆检查,将环境变量
HEAPCHECK
定义为您想要的堆检查类型:normal
(正常),strict
(严格), 或draconian
(苛刻)。例如,堆检查
/bin/ls
:$ HEAPCHECK=normal /bin/ls
% setenv HEAPCHECK normal; /bin/ls # csh -
对于部分代码堆检查,需要修改代码。
对于要进行堆检查的每段代码,通过创建一个
HeapLeakChecker
对象(该对象接受描述性标签作为参数)括起代码,并在要检查的代码末尾调用check.NoLeaks()
。这将验证在代码段末尾分配的内存不会多于在开始时分配的内存。
要实际打开堆检查,请将环境变量HEAPCHECK
设置为local
。
下面是第二个用法的一个例子。
如果 Foo()
泄漏任何内存(即它分配的内存在返回时没有释放,下面的代码将会死亡:
HeapProfileLeakChecker checker("foo");
Foo();
assert(checker.NoLeaks());
分配检查器对象时,它会创建一个堆概要文件。当调用 checker.NoLeaks()
时,它将创建另一个堆概要文件,并将其与之前创建的概要文件进行比较。
如果新配置文件指示内存增长(或者使用 checker.SameHeap()
表示内存分配的任何更改)NoLeaks()
将返回 false
,程序将中止。还将打印一条错误消息,说明如何运行 pprof
命令以获得对实际泄漏的详细分析。
有关更多信息和示例,请参阅 heap-checker.h
中的 HeapProfileLeakChecker
类的注释和 heap-checker_unittest.cc
中的代码。
禁用已知泄漏的堆检查
有时你的代码存在你知道并愿意接受的泄漏。 你希望堆检查器在检查程序时忽略它们。你可以通过使用适当的堆检查对象将有问题的代码括起来 来实现这一点:
#include
...
void *mark = HeapLeakChecker::GetDisableChecksStart();
<leaky code>
HeapLeakChecker::DisableChecksToHereFrom(mark);
一些 libc
例程分配内存,可能需要以这种方式 "禁用"
。
随着时间的推移,我们希望将这些例程的正确处理编码到堆检查器库代码中,这样应用程序就不必担心这些例程,但是这个过程还没有完成。