一、mtrace分析内存泄露
mtrace(memory trace),是 GNU Glibc 自带的内存问题检测工具,它可以用来协助定位内存泄露问题。它的实现源码在glibc源码的malloc目录下,其基本设计原理为设计一个函数 void mtrace (),函数对 libc 库中的 malloc/free 等函数的调用进行追踪,由此来检测内存是否存在泄漏的情况。mtrace是一个C函数,在<mcheck.h>里声明及定义,函数原型为:
函数会为那些和动态内存分配有关的函数(譬如 malloc()、realloc()、memalign() 以及 free())安装 “钩子(hook)” 函数,这些 hook 函数会为我们记录所有有关内存分配和释放的跟踪信息,而 muntrace() 则会卸载相应的 hook 函数。
基于这些 hook 函数生成的调试跟踪信息,我们就可以分析是否存在 “内存泄露” 这类问题了。
mtrace 机制需要我们实际运行一下程序,然后才能生成跟踪的日志,但在实际运行程序之前还有一件要做的事情是需要告诉 mtrace (即前文提到的 hook 函数)生成日志文件的路径。
设置日志生成路径有两种,一种是设置环境变量: 另一种是在代码层面设置:就是储存检测结果的文件的名称。
从上述代码中,我们希望能够在程序开始到结束检查内存是否泄漏的问题,例子简单,一眼就能看出存在内存泄漏的问题,所以我们需要验证 mtrace 是否能够检查出来内存泄漏问题,且检查的结果如何分析定位。 生成可执行文件。
程序运行结束,会在当前目录生成 test.log 文件,打开可以看到一下内容:
从这个文件中可以看出中间三行分别对应源码中的 malloc -> free -> malloc 操作。解读:https://www.bilibili.com/read/test 指的是我们执行的程序名字,[0x] 是第一次调用 malloc 函数机器码中的地址信息,+ 表示申请内存( - 表示释放),0x21ed450 是 malloc 函数申请到的地址信息,0x64 表示的是申请的内存大小。由此分析第一次申请已经释放,第二次申请没有释放,存在内存泄漏的问题。
通过使用 "addr2line" 命令工具,得到源文件的行数(通过这个可以根据机器码地址定位到具体源码位置)
mtrace + 可执行文件路径 + 日志文件路径 执行,输出如下信息:
Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。Valgrind的体系结构如下图所示
最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc() / free() / new / delete 的调用都会被捕获。
所以,它能检测以下问题:对未初始化内存的使用;读/写释放后的内存块;读/写超出malloc分配的内存块;读/写不适当的栈中内存块;内存泄漏,指向一块内存的指针永远丢失;不正确的malloc/free或new/delete匹配;memcpy()相关函数中的dst和src指针重叠。
和 gprof 类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和 gprof 不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。
Callgrind 收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行 cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate 可以把这个文件的内容转化成可读的形式。
Cache 分析器,它模拟 CPU 中的一级缓存 I1,Dl 和二级缓存,能够精确地指出程序中 cache 的丢失和命中。如果需要,它还能够为我们提供 cache 丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。
它主要用来检查多线程程序中出现的竞争问题。Helgrind 寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。
Helgrind 实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind 仍然处于实验阶段。
堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。
Massif 能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。
此外,lackey 和 nulgrind 也会提供。Lackey 是小型工具,很少用到;Nulgrind 只是为开发者展示如何创建一个工具。
本文的重点是在检测内存泄露,所以对于valgrind的其他工具不做过多的说明,主要说明下Memcheck的工作。Memcheck检测内存问题的原理如下图所示:
Memcheck 能够检测出内存问题,关键在于其建立了两个全局表。
valgrind 将内存泄漏分成 4 类:
这里遗失的是 root 表针(是确立泄露类型),造成 root 储存的 next 表针变成了间接性泄露。间接性泄露的运行内存毫无疑问也要修补的,但是一般会伴随着 确立泄露 的修补而修补。
【文章福利】小编推荐自己的Linux内核技术交流群:【】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)
为了更好地在出难题时要详尽打印出出去栈信息内容,实际上大家最好是在编译程序时加上 -g 选择项。如果有动态性载入的库,必须再加上 ,不然假如发觉是动态性载入的库发生泄露,因为动态库被卸载掉了,造成找不到符号表。编码编译程序提升,不建议应用 -O2既之上。-O0很有可能会造成运作变慢,建议使用-O1。
使用valgrind命令来执行程序同时输出日志到文件
参数说明:
第二段是对堆内存分配的总结信息,其中提到程序一共申请了1次内存,其中0次释放了,4 bytes被分配()。
在head summary中,有该程序使用的总heap内存量,分配内存次数和释放内存次数,如果分配内存次数和释放内存次数不一致则说明有内存泄漏。
第三段的内容描述了内存泄露的具体信息,其中有一块内存占用4字节(),在调用malloc分配,调用栈中可以看到是func函数最后调用了malloc,所以这一个信息是比较准确的定位了我们泄露的内存是在哪里申请的。
最后这一段是总结,4字节为一块的内存泄露。
problem1: 指针pt申请了空间,但是没有释放; problem2: pt申请了5个int的空间,p经过5次循环已达到p[5]的位置, 时,访问越界(写越界)。(下面valgrind报告中 Invalid write of size 4)
problem1: 读越界 (下面valgrind报告中 Invalid read of size 4 )
报告如下,
申请释放接口不匹配的报告如下,用malloc申请空间的指针用free释放;用new申请的空间用delete释放():
内存检测方式无非分为两种:
1、维护一个内存操作链表,当有内存申请操作时,将其加入此链表中,当有释放操作时,从申请操作从链表中移除。如果到程序结束后此链表中还有内容,说明有内存泄露了;如果要释放的内存操作没有在链表中找到对应操作,则说明是释放了多次。使用此方法的有内置的调试工具,Visual Leak Detecter,mtrace, memwatch, debug_new。2、模拟进程的地址空间。仿照操作系统对进程内存操作的处理,在用户态下维护一个地址空间映射,此方法要求对进程地址空间的处理有较深的理解。因为Windows的进程地址空间分布不是开源的,所以模拟起来很困难,因此只支持Linux。采用此方法的是 valgrind。
原文作者:一起学嵌入式
到此这篇hprof文件分析内存泄露(如何分析hprof文件)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/32541.html