前一阵子在项目中使用tcmalloc的heap-checker查找内存泄露的问题,今天翻译一下官方文档的一篇heap-checker相关的文章,由于时间有限,没有完全翻译完,只翻译了比较重要的部分,后续将会补全。
在Google,我们使用heap checker检查C++程序的内存泄露。使用heap checker分三步:将lib库链接到应用中;运行代码;分析输出结果。
链接tcmalloc
heap-checker是tcmalloc的一部分,为了在应用程序中安装heap checker,在程序链接阶段需要加入-ltcmalloc。但上面这种方法并不是唯一的方式,我们可以使用LD_PRELOAD宏在程序运行时加入tcmalloc:
% env LD_PRELOAD="/usr/lib/libtcmalloc.so"
上面这种方式并没有打开heap checking;仅仅将代码插入到程序中。因此,在开发阶段我们建议使用链接tcmalloc库的方法,这也是我们在Google使用的方法(然而,我们也可以使用环境变量开启profiler,因此通过链接tcmalloc库并不是必须的。)。需要注意的是为了使用heap checker必须使用tcmalloc内存分配库。
运行代码
注意:由于一些安全原因,对于setuid程序,heap profiler在使用的过程中并不会写文件。
对整个程序进行内存泄露检查
我们推荐使用heap-checker的方式是“全程序”模式。在这种模式下,heap-checker在main()函数开始之前跟踪内存分配,在程序介绍的时候再次检查。如果发现有内存泄露(任何已经分配的内存在程序结束的时候并没有对象引用它们),程序将会中断(通过exit(1))并且打印怎样跟踪内存泄露的信息(使用pprof)
heap-checker会记录每一次内存分配的调用栈,因此开启heap-checker会导致程序的内存增长并影响程序的性能。
下面介绍怎么开启全程序模式的内存泄露检查:
定义环境变量 HEAPCHECK 声明内存泄露检查的模式。例如下面的方式:
% env HEAPCHECK=normal /usr/local/bin/my_binary_compiled_with_tcmalloc
没有其他的操作了。
需要注意的是heap-checker使用heap-profiling的架构,因此不可能同时运行heap-checker和heap-profiler。
内存检查的特性
如下是在使用全程序内存检查的合法变量:
- minimal
- normal
- strict
- draconian
“minimal”的模式开始内存泄露检查尽可能晚,意味着你可以在初始化例程存在一些内存泄露(在main()函数前),但并不会让heap-checker记录。如果你在全局初始化时候存在内存泄露,“minimal”模式就很适合。否则,你应该使用更加严格的模式。
“normal”模式跟踪存活对象(live objects)并报告其内存泄露信息(在程序结束时,无法通过存活对象找到的内存空间都是内存泄露)
“strict”模式与“normal”模式很相似,但“strict”模式会监控全局变量析构函数的内存泄露。例如,如果你有一个全局变量,在运行时申请了部分内存,在析构函数中忘记释放内存,在“strict”模式下将会被监控到,在“normal”模式下将不会。
“draconian”模式适合想清晰了解应用程序内存管理的情况,其希望heap-checker去帮助他们优化内存管理。在“draconian”模式,heap-checker并不会只检查存活对象,只要有内存泄露,其都会报告。
“normal”模式是最常用的模式。
as-is”是一个更加灵活的模式;它允许你自定义heap-checker的一些特性。“local”激活“显式heap-check指令”,但并不会开启任何全程序内存泄露检查。
用于调试内存泄露的小方法
工作原理
当一个HeapLeakChecker对象被构造的时候,它会在tmp文件夹输出一个名为