1。内存泄漏简介在工作中,动态内存分配很常见,而伴随动态内存分配而来的最大问题就是“内存泄漏”。所谓“内存泄漏”,就是申请了内存,却忘记归还给系统。长此以往,系统可以分配的内存越来越少。一旦出现这样的问题,将很难发现。原因很简单,程序是人写的,写的人忘记分配到哪里了,没有释放,系统忍不住随便回收内存。一旦出现“内存泄漏”,尤其是一些生命周期较长的程序,从系统的角度来看,可用内存莫名其妙地越来越少,隐喻系统出现了漏洞。记忆通过这个洞“泄露”而消失。2、mtrace使用介绍一旦发现系统出现这种“症状”,当务之急就是找出自己在代码中哪里忘记归还了动态分配的内存。“内存分配跟踪(malloctracing)”机制是帮助检查“内存泄漏”的好帮手。本文将介绍这个工具的使用。这个工具习惯上简称为mtrace。下面将直接使用mtrace来引用这个工具。mtrace工具的主要思想是在调用内存分配和释放的函数中加载“钩子”函数,利用钩子函数打印的日志帮助分析内存使用是否有问题。这个工具的使用包括两部分,一是修改源码加载钩子函数,二是运行修改后的程序生成一个特殊的日志文件,然后使用mtrace工具分析日志判断是否存在内存泄漏,定位到可能发生内存泄漏的代码位置。mtrace是Glibc的一部分,不需要特殊安装。mtrace钩子函数定义如下:#includevoidmtrace(void);voidmuntrace(void);其中mtrace()用于启用内存分配跟踪,而muntrace()用于取消内存分配跟踪。具体方法是mtrace()函数会为那些与动态内存分配相关的函数(如malloc、realloc、memalign、free)安装“钩子”函数,这些钩子函数会记录所有相关的内存分配和释放。跟踪信息,muntrace()会卸载相应的钩子函数。根据这些钩子函数产生的调试跟踪信息,可以分析是否存在“内存泄漏”等问题。2.1.示例代码示例代码如下:demo_mtrace_memleak.c/*gccdemo_mtrace_memleak.c-odemo_mtrace_memleak-g-Wall*/#include#include#includeintmain(intargc,char**argv){mtrace();字符*p=malloc(16);免费(p);p=malloc(32);跟踪();return0;}如上,两次malloc申请了内存的操作,但是只释放了一次,所以会造成“内存泄漏”。看看mtrace是否能得出同样的结论。2.2.mtrace命令行要使用mtrace机制,需要在生成tracelog之前实际运行程序,但是在运行程序之前,需要定义并导出一个环境变量MALLOC_TRACE来记录分析日志,如下图。exportMALLOC_TRACE=./memleak.log以上结果是告诉mtrace在生成日志信息时在当前路径下创建一个名为memleak.log的文件,并将日志输出到这个文件中。运行程序顺序后,查看memleak.log内容如下:=Start@./demo_mtrace_memleak:[0x7f6ca1400738]+0x7fffc93796a00x10@./demo_mtrace_memleak:[0x7f6ca1400748]-0x7fffc93796a0@./demo_mtrace_memleak:[0x7f6ca1400752]+0x7fffc93796c00x20=End这个文件的Therearethreelinesof"valid"records(排除第一行=Start和最后一行=End),对应源码的malloc->free->malloc操作。具体格式以第一行@./demo_mtrace_memleak:[0x7f6ca1400738]+0x7fffc93796a00x10为例。./demo_mtrace_memleak显然指的是正在运行的可执行文件的名称。[0x7f6ca1400738]这里的值是对应代码中第一次调用的地址,但是注意这里是机器码的地址,编译可执行程序的时候用-g带上调试信息就好了,所以可以使用工具addr2line,检索源文件的行数。具体方法如下:#addr2line-f-e./demo_mtrace_memleak0x00748main/mnt/d/MingruiZhou/tinylab/demo_mtrace_memleak.c:11确实在第11行,然后有个符号+,说明这一行对应内存的分配,反之亦然——意味着释放。之后是一个值0x7fffc93796a0,这是malloc()函数分配的内存的首地址。最后一个是0x10,换算成十进制就是16,就是分配内存的大小。了解具体格式后,可以从三行有效日志中获取,因为第一行是allocation,分配的内存首地址是0x7fffc93796a0,第二行释放的内存首地址也是0x7fffc93796a0,这自然意味着它是一对。相互抵消,不存在内存泄漏。第三行分配的内存首地址为0x7fffc93796c0,如果没有匹配到释放日志,则说明存在“内存泄漏”。在实际工作中,场景代码绝不会只是几行代码。系统提供了一个名为mtrace的命令行工具来帮助完成日志分析。赶快试试吧。输入以下命令:#mtrace./demo_mtrace_memleak$MALLOC_TRACEMemorynotfreed:----------------AddressSizeCaller0x00007fffc93796c00x20at0x7f6ca1400752结果的输出已经说明了一切。mtrace工具至少需要两个参数,一个是生成的可执行程序文件的路径,一个是日志文件的路径。mtrace还可以帮助发现“doublefree”问题。示例如下:/*gccdemo_mtrace_double_free.c-odemo_mtrace_double_free-g-Wall*/#include#include#includeintmain(intargc,char**argv){字符*s=NULL;跟踪();s=malloc(32);免费;免费;//<--双重释放muntrace();return0;}结果如下:#gccdemo_mtrace_double_free.c-odemo_mtrace_double_free-g-Wall##exportMALLOC_TRACE=./double_free.log#./demo_mtrace_double_free#mtrace./demo_mtrace_double_free$MALLOC_TRACE-0x00007fffd74056a0Free4从未被分配0x7f07ff60075c没有内存泄漏。#addr2line/-fdemo_mtrace_double_free0x75cmain/mnt/d/MingruiZhou/tinylab/demo_mtrace_double_free.c:19虽然最后没有准确找到错误线,但是已经接近错误线了。邮箱:MingruiZhou@outlook.com