Linux进程内存用量分析之堆内存篇

导语
本文将介绍几种内存泄漏检测工具,并通过实际例子介绍一种分析堆内存占用量的工具和方法,帮助定位内存膨胀问题 。
背景进程的内存管理是每一个开发者必须要考虑的问题,对于C++程序进程来说,出现问题很多情况下都与内存挂钩 。进程崩溃问题通常可以使用gdb等调试工具轻松排查并解决 。而对于进程内存膨胀这类问题,原因通常有三个:
1.内存泄漏 。
2.分配器管理的空闲内存较多而造成的内存空洞 。
3.有未统计使用的未知内存占用 。
内存泄漏问题可以使用一些工具来检测 。但是对于后两种问题,却一直没有比较通用的方法去确定 。本文将介绍几种内存泄漏检测工具,并通过实际例子介绍一种分析堆内存占用量的工具和方法,帮助定位内存膨胀问题 。
常见内存问题的分析方法对于内存泄露问题,目前已经有较成熟的工具进行检测,这里简单介绍两个工具:AddressSanitizer和Valgrind 。
AddressSanitizer是google开源项目,可以用来检测内存泄漏和其他导致进程崩溃的内存问题 。它的优势在于造成的额外CPU占用很小,但是需要重新编译项目,并且在编译的时候添加-fsanitize=address选项 。在程序运行时如果有任何内存问题,就会终止进程并且打印出详细的错误信息 。如果进程存在内存泄漏会在进程结束后,打印出所有泄漏的内存大小和申请这块内存的调用栈,如下图所示:

Linux进程内存用量分析之堆内存篇

文章插图
 
AddressSanitizer检测内存泄漏
另一个工具Valgrind的优势在于不需要重新编译,只需要在运行时加上valgrind --leak-check=yes即可 。但是它的额外CPU开销会更大,大约是AddressSanitizer的十倍,功能上也不及AddressSanitizer完善 。下表是两种工具功能和性能的比较:
Linux进程内存用量分析之堆内存篇

文章插图
 
AddressSanitizer与Valgrind对比
这两种工具不光能够检测内存泄漏,对于堆栈溢出等问题也有比较好的效果 。对于这两个工具更具体的介绍可以参照官方的使用文档:
AddressSanitizer:https://github.com/google/sanitizers/wiki/AddressSanitizer
Valgrind:http://valgrind.org/
而对于后两种原因,我们需要根据不同的分配器区别看待 。常见的分配器有glibc默认的ptmalloc,google维护的tcmalloc以及facebook维护的jemalloc等 。后两者都自带了内存分析工具(Heap Profiler),可以检测内存泄漏,也可以打印出详细的内存分配情况,对上述三个问题都有比较完善的排查方法,有兴趣可以查看官方文档,都讲得比较详细,这里不再介绍 。而glibc默认的ptmalloc却不自带这样的工具,一种排查方法是去了解ptmalloc的实现和结构以后编写程序或者gdb脚本去分析进程的内存结构,我们接下来要介绍的一种内存分析工具就是以这种方法实现的 。
针对ptmalloc的堆内存内用分析1.环境58自研的搜索引擎Esearch底层使用C++实现 。Esearch在内存管理方面针对不同的场景会有不同的策略 。对于对象生命周期有规律,高频分配的场景,Esearch实现了定制的内存池进行管理,并且这些内存都会在日志中统计占用量 。而对于对象生命周期不确定,大小不确定的场景,内存池的代价可能高于通用分配器(new/malloc),所以直接使用通用内存分配器来分配 。
对于通用分配器的选择,目前Esearch使用的是glibc2.12环境下默认的ptmalloc 。之所以未使用tcmalloc或者jemalloc是因为经测试后发现后两者在常见场景下内存占用比ptmalloc要高,而且Esearch中对于内存分配热点已经使用了定制内存池,后两种分配器的优势其实并不明显 。对于Ptmalloc完整结构的介绍可以阅读源码或者参阅华庭的《glibc内存管理ptmalloc2源代码分析》,这里只在用到时阐述一下原理,不做过多的介绍 。
接下来我们通过一个例子来了解如何分析堆内存的用量 。现在有一个realtime_searcher进程如下:
 
运行中的realtime_searcher进程
【Linux进程内存用量分析之堆内存篇】可见进程占用总物理内存27G,其中SHR内存占用18G,剩下的物理内存 约9G 。
2.工具介绍
这里介绍一个非常强大的内存分析工具——core_analyzer 。这是一个基 于core文件的内存分析工具,由Michael Yan开发和维护并且开源在github上 。利用它可以对glibc层的ptmalloc结构进行分析,还原进程真实的内存结构 。目前core_analyzer支持的glibc2.3,2.4,2.5,2.12-2.23版本下的ptmalloc实现,这些版本对应的ptmalloc结构其实都大同小异 。


推荐阅读