真正的内存泄漏
真正的内存泄漏可以利用valgrind来进行检测。编译exefile时加上-g参数,然后执行如下命令
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --error-limit=no --track-fds=yes --log-file=val.log ./exefile
当进程正常结束后,查看val.log文件,其中会分析出确定泄漏,可能泄漏和程序退出时仍未释放的内存。
对于服务进程,需要捕获TERM信号做推出前的资源释放操作,否则会混入大量干扰信息。
非内存泄漏的内存持续增加
对于某些“内存持续增加”的情况,有可能并不是内存泄漏,而是在运行期间逻辑上未及时清理某些内存。这种用valgrind是查不出来的,因为在进程退出前,这些内存会被正确清理。这时可以使用gperftools里面的tcmalloc来帮助查找未被清理的内存是在哪个函数申请的。方法如下:
# 用动态载入tcmalloc的方式执行你自己的可执行程序(加-g编译选项)。
# 下面三个参数的意思是检查堆内存,每当累计分配内存(无论是否释放)达到100MB时,将当前堆内存快照输出到memtm.xxxx.heap的文件中
LD_PRELOAD=libtcmalloc.so HEAPCHECK=strict HEAPPROFILE=memtm HEAP_PROFILE_ALLOCATION_INTERVAL=100000000 ./exefile
# 之后持续运行一段时间,如果是服务进程,需要压测一段时间,假设压测到稳定阶段的内存快照文件是memtm.0005.heap
# 继续压测一段时间,直到确认内存已经开始非正常增加,停止,假设最后的内存快照文件是memtm.0010.heap.
# 然后利用gperftools通过对比这两个快照文件,分析diff部分是否有内存增长,增长主要来自于哪些函数调用
# (分析结果中有每个函数对应分配的增长内存总量和占比,顺藤摸瓜可以基本定位是哪部分内存分配后未及时释放)。
# 之所以不使用memtm.0001.heap作为对比base,是因为对于一些程序,初期会显示或者隐式的使用一些缓存,这部分也会导致一定的内存增长。
# 等到进入稳定阶段后,这些缓存基本达到稳定状态,不再增长了。这时再对比可以去掉正常缓存的干扰。
pprof --pdf --base=memtm.0005.heap ./exefile memtm.0010.heap > diff.pdf
当然也可以通过静态链接tcmalloc库,针对性检查某个代码块的内存分配情况,不过通常上面的方法已经足够了。
参考
- [[https://www.ibm.com/developerworks/cn/linux/l-mleak/index.html|如何在linux检测内存泄漏]]
- [[https://blog.csdn.net/jhzhou/article/details/7245992|Google Heap Profiler使用方法]]