“different,but not less. 不同,但也不差!”
前记
出现内存错误,查问题是一方面,更多的是需要考虑,以后写代码如何不出现内存错误。总结很关键。
- 《Linux多线程服务端编程使用muduo网络库》这本书说的是RAII技术(后期研究下,做一些实践)。
- 自己的总结:new 出的内存,不进行类之间的长途传递,若出现这种情况,需要思考下是否真的有必要如此。比如,自己看到的一种比较难受的管理方式:在调用中,new出一个对象A传递指针到另外一个对象B中,并且,最后在对象B析构函数中去释放了A。这种情况,我现在认为的解决办法是传递必要参数到对象B中去new,并且在对象B中的析构中去释放。便于管理。
一、简介
-
最近在查程序的内存泄漏问题,是在tcmalloc内存分配的基础上,关于tcmalloc是介绍:主要参见两篇博文:
(1) 官方文档介绍,主要介绍原理和为什么tcmalloc比glic的快:
tcmalloc
(2) 知乎之前看到一篇分析原理的,楼主添加了很多自己画的图分析,可以当做翻译看:
图解tcmalloc
-
google 出了一套gperftools工具 可以分析cpu占用(profiler.h),分析内存占用(heap-profiler.h),检查内存泄漏(heap-checker.h) ,这里主要讲后面两者的使用
-
内存检查的基本原理是:检查运行的代码每个函数中的内存使用情况,生产内存快照文件(即我们需要得到的分析原始数据),内存快速增长的情况下会生成很多的内存快照,后期分析可以前后对比看是什么堆栈的内存分配最多。
二、程序环境配置
-
tcmalloc环境和库 (http://github.com/gperftools/gperftools) 源码安装
-
生成 cpu/内存 采样数据的分析工具:
(1)pprof工具使用需要安装 graphviz(http://www.graphviz.org) 和 ghostscript(www.ghostscript.com)
三、内存分析方式
-
情况1:程序本身链接了tcmalloc库,在不重新编译的情况下,使用环境变量进行数据采集
#启动程序的方式: HEAPCHECK=normal HEAPPROFILE=prefix ./test //需要前台启动程序 #第一个参数HEAPCHECK表示检查的严格程度 HEAPPROFILE参数表示生产的内存快照 #默认生成的内存快照文件在/tmp 目录下。
2. 情况2:在程序中添加接口,以便后期线上直接调用分析
使用HeapProFilerStart("prefix") "prefix"是生成文件的前缀和 HeapProFilerStop 接口
1.在头文件 <gperftools/heap-profiler.h> 中。 2.可以使用http接口或者telnet 使用方式:程序可以前台,也可以后台,启动之后,发命令或者请求让其执行HeapProFilerStart ,
然后复现程序内存上涨的情况,接着在程序的当前目录可以看到 prefix_heap.0001.heap (prefix为start传入的前缀),
随着内存上涨,会持续的生很多的内存快照,差不多的时候stop。然后开始使用获得的内存快照分析。
内存快照的分析方式:
-
单个快照查看: 使用pprof生成pdf图
执行命令: pprof --pdf ./test ./prefix_heap.0001.heap > 1.pdf
-
对比前后快照查看方式:依然使用pprof生成pdf
执行命令:pprof --pdf --base=prefix_heap.0001.heap ./test ./prefix_heap.0002.heap > 1.pdf
根据生成的pdf文件查看,主要关注百分比占比很高的堆栈,然后根据这个堆栈去程序中分析申请的内存是否都释放。
对比生成的数据可以很明显看出内存上涨是哪些函数调用产生的,但是需要根据实际的代码去分析到底有没有内存没有释放。
3.检查片段代码的内存泄漏 :以上两种方式只是用来获取程序内存分配情况的,接下来的方式可以直接检查某个函数的使用到底有没有内存泄漏
(1)需要在代码中嵌入检查: 使用头文件<gperftools/heap-checker.h> 中的类 HeapLeakChecker
(2)添加的代码片段:
详细参考:官网:
http://goog-perftools.sourceforge.net/doc/heap_checker.html 博文:
http://pages.cs.wisc.edu/~danb/google-perftools-0.98/heap_checker.html
HeapLeakChecker heap_checker("test_foo"); { code that exercises some foo functionality; this code should preserve memory allocation state;
} if (!heap_checker.SameHeap()) assert(NULL == "heap memory leak");
(3)启动程序: HEAPCHECK=normal ./test
(4)输出:
-
若没有内存泄漏:
No Leaks found for check "test_foo"
-
若存在内存泄漏:
Leak check _main_ detected leaks of 1136 bytes in 2 objects The 2 largest leaks: Leak of 568 bytes in 1 objects allocated from: @ 7fa7f4197dd2 If the preceding stack traces are not enough to find the leaks, try running THIS shell command: pprof ./bin/exe "/tmp/exe.7539._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --pdf If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false,
it mi Exiting with error code (instead of crashing) because of whole-program memory leaks
以上就是内存泄漏的情况下的输出,这个输出不够详细,无法看出泄漏情况,可以参照他的建议执行pprof命令:
pprof ./bin/exe "/tmp/exe.7539._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --pdf > 1.pdf
查看pdf,同上分析内存分配堆栈来查找内存泄漏的问题。