文章插图
地址块引用链
上图中显示出多条调用链,这是由于对一个chunk的引用不只是有指针指向chunk的首部的情况,还可能指向chunk的内部 。取上图中最后一条调用链举例,其意义为一块56大小的chunk在偏移量为40的位置指向了4292967280这个块中地址为0x7f8f802ec6a4的地方,而56大小块的虚指针为_vptr,这样,我们就可以通过gdb得到这个虚指针地址对应的符号为
使用gdb得到虚指针的符号表
从而得知这个56大小的块对应的类是某个ArrayReader,该类作用为读取正排索引值,内存结构如下图 。可以得知偏移量为40的地址存放的是一个指针指向打开的索引文件 。于是验证了前边的猜测 。
文章插图
ArrayReader数据结构
上边属于找到了直接引用该chunk的对象,而有些对象的引用可能是间接引用,中间通过好几层才引用到要查找的chunk,例如下图中就找到了一条间接引用链,但是定位方法跟直接引用是一样,通过gdb命令info symbol可见上层vptr指向LRUCache类,而所查询的chunk其实就是LRU缓存中双向链表的节点 。
文章插图
多层引用链以及顶层虚指针类型
如果还对结果存疑,可以使用Memory Pattern Analysis功能进一步验证,这个功能会打印出所给地址空间存储的内容,可以对照源码一一确认存储数据的值 。例如确认一个HashNode的结构在内存中的布局:
文章插图
内存存储值与代码中的结构一一对应
通过这种方式,对占用内存较大的几类chunk一一进行排查,便可知晓是那些对象在占用内存 。但是不可避免有一些不同的类的大小却一样,前边的假设会将它们看做同一个类,这就需要对这种大小的chunk进行抽样排查,可以尝试写一些脚本辅助排查 。
4.注意事项
上文中介绍了使用core_analyzer分析堆内存占用的方法 。虽然core_analyzer功能很强大,但是也存在一些缺陷,使用时有些注意事项需要了解 。
1)当前的原版core_analyzer最多支持到32个线程,而igit上改动过后的版本支持256个线程的进程,这部分以后可能会考虑优化,使其能够根据线程数自我扩展 。
2)引用搜索功能打印的结果只是潜在的引用链,因为对于例如某变量值刚好等于查询地址这些情况,core_analyzer并不能区分 。所幸的是这样的情况极少 。
3)使用时候一定要有耐心,对于较大的core文件core_analyzer确实需要耗费较长的时间处理 。
4)[8]和[9]功能不要轻易尝试,很慢,而且跑不出来 。
5)内存泄漏检测功能形同虚设,结果难以理解 。建议还是使用上文中介绍的两种工具来检测内存泄漏 。
6)Core_analyzer的使用前提是对应用的代码已经有比较详细的了解,至少要能分辨出代码中哪些内存的分配是调用了mmap而不是malloc 。
虽然core_analyzer的使用并不友好,但是目前为止,暂时还没有发现有其他工具有如此强大的功能 。使用其他工具诸如libheap等来定位占用内存的对象无疑等同于大海捞针 。所以可以说core_analyzer的功能之强大足以掩盖其用户体验差的问题 。
内存优化建议通过上述过程,我们已经能够分析堆内存的占用并且定位到内存膨胀的原因,接下来就可以针对这些原因对症下药 。由于每个应用的复杂性和场景都不尽相同,这里只提一些简单的优化思路 。
对于内存空洞现象比较严重的情况,由于ptmalloc自身的策略原因,不适合分配长生命周期的内存,可以考虑尝试tcmalloc等其他分配器 。如果非用不可,那一定要记得后分配的内存要先释放,这样才能够使堆收缩 。如果分配区的堆顶不释放,那下边的内存就都不会释放 。这在多线程进程中其实比较难实现,因为不能保证内存的申请和释放在同一个线程 。
另外可以通过调用malloc_trim()函数来手动收缩堆 。官方文档上介绍只能够收缩主分配区的堆顶,但是经实践和源码分析发现,它其实可以遍历所有的分配区,并且对其中的空闲内存调用madvise建议操作系统回收该部分内存 。虽然操作系统是否真的回收就是另一回事了,但是在实际的实践后发现,对内存空洞现象的改善效果明显,可以酌情使用 。
对于使用中的内存较多的情况,这类情况比较复杂,需要多加分析,寻找优化点 。比如底层的数据结构是否有优化空间,尤其是对于实例比较多的类,例如哈希节点或者链表节点这些类,可能几个字节的优化就能带来较高的收益 。还有在内存中尽量只保留有用的对象,无用的对象尽早释放掉,这样有利于内存空间的复用,一个例子就是过期的in-memory缓存要及时释放掉 。
推荐阅读
- linux日志关键字检索&处理
- linux中多种查看系统时间的命令
- 本地Docker Jenkins构建dotcore web应用到Linux服务器 Docker上
- 在 Linux 终端快速检测网站是否宕机的 6 个方法
- linux run level 为何物
- SQLite使用内存数据库
- Linux下如何使用crontab来定时执行脚本任务?
- Linux的图形用户界面-你会选择哪个?
- Linux的Cache和Buffer理解
- 不知道这十项Linux常识,别说自己是运维工程师