利用Valgrind和gperftools解决内存问题

近期,在对于系统进行性能测试,暴露一些问题。在定位过程中尝试使用一些工具,有效的帮助识别问题,并且解决了问题。由于问题比较典型,分享给大家,以便大家遇到类似问题时,借鉴参考。

工具介绍


1 Valgrind

Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。


valgrind的结构图

其中:Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等

2. Heap Checker

google-perftools 是一款针对 C/C++ 程序的性能分析工具,它是一个遵守 BSD 协议的开源项目。提供四个工具:包括tcmalloc,heap-profiler, heap-checker,CPU profiler。heap-checker专门检测内存泄漏,heap-profiler则是内存监控器,可以随时知道当前内存使用情况(程序中内存使用热点),当然也能检测内存泄漏。

3. getrusage

linux提供rusage结构,可以描述业务进程内存使用情况,类似ps aux。

利用工具定位解决内存问题


三个典型问题,处理过程如下:

1. new expression导致的内存泄漏

1.1 问题描述

从性能测试时,统计主机可用内存减少,明显存在内存泄漏。


图2-1 12分钟主机内存变化情况

1.2 问题定义分析

在性能测试主机安装Valgrind来进行内存泄漏分析。利用Valgrind启动程序:

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --trace-children=yes ZSmartOCPro IN 2>&1| tee ZSmartOCPro_IN.txt

发送业务消息,然后退出业务进程,可以生成分析文件。

图2-2 三个业务包内存统计摘要

图2-3 九个业务包内存统计摘要
图2-4 可能性能泄露点

从对比数据看,肯定存在内存泄漏。在业务代码中,为了提高性能,设计上采用new expression(参考:http://en.cppreference.com/w/cpp/language/new)内存模式,即将使用内存管理起来,重复使用,减少频繁申请内存,从而提高性能。

图2-5 new expression

DataNode管理的数据和其本身都是采用这种方式。理论上次内存不存在释放,也不应该有泄露。从分析日志来看,因为DataNode中存在map和vector中内存没有失败。map和vector的内存有stl自行管理,在DataNode对应的Clear函数,增加map和vector的clear调用,来释放内存。测试时发现,这样的修改并没有用,why?
map和vector的内存模式也是new expression模式,clear只是清理计数器,并没有减少size。只有在容器析构的时候,才会释放内存。
修改的方式:在调用DataNode的Clear方法的地方改为显示调用他的析构函数~DataNode()方式来析构其内部的map和vector成员。

1.3 测试验证

图2-6 12分钟内存变化曲线

对比前面曲线,减少满了一些,说明修改有效。是否存在其他内存泄漏情况,使用Valgrind继续进行测试。


图2-7 3个消息的泄露分析汇总
图2-8 12个消息的泄露分析汇总

因为第三方库使用问题,存在内存泄漏,但是并其并没有发生变化。分析其输出其他内存信息,也没有可疑的地方,问题的彻底解决陷入迷茫。

2 double linked导致单元测试core

2.1 问题描述

在解决问题1时,曾经提交调用DataNode的Clear方法。在跑单元测试用例时,有少量的代码会core,位置为与修改无关的python调用处:


图2-9 double linked 导致core

2.3 问题解决

以往的经验,double linked一般都是非法内存读写引起。使用Valgrind是可以检查出,确实DataNode相关的内存确实存在非法读写。
以前实现方式:DataNode管理类和对应buffermanager都是单例的,并且通过静态变量,而其析构不受程序员控制,其内存又有关联系统导致。
修改:DataNode管理类保持单例不变,buffermanager修改为DataNode管理类的成员,由其负责buffer的生命周期。
修改后,运行单元测试正常,问题解决。

3 内存问题终极定位

3.1 问题描述

如图2-6所示,在解决了内存泄漏以后,内存还是减少而且无法用valgrind检测,是为什么呢?

3.2 问题分析

valgrind的泄露是检测从启动到退出,检测出无主内存泄漏情况。而且使用Valgrind运行业务程序,程序存在很严重的卡顿,不适合长时间和大量压力运行。从分析来看,业务进程在运行过程中,可能存在不断申请内存,在退出过程中释放。例如map或者vector等容器。
初步探索:

  • 1 也尝试用getrusage来测试成内存,能发现内存变大,却无法定位位置。getrusage显示maxrss是以块方式显示。


    getrusage测试
  • 2 走查怀疑代码。因为代码量大,无目的的走查是很低效的。
  • 3 利用二分法屏蔽组件方式来缩小范围后,在走查。屏蔽代码可能导致业务不通,未必能找出问题。
    需要另辟蹊径,有同事推荐gperftools来一试。

3.3 问题解决

google-perftools提供heap_checker功能(https://gperftools.github.io/gperftools/heap_checker.html)。

  • 1 首先是安装gperftools。
  • 2 重新编译业务代码添加 -ltcmalloc 链接tcmalloc库。
  • 3 启动平台,因为进程需要单独启动。

env HEAPCHECK=normal HEAP_PROFILE_ALLOCATION_INTERVAL=104857600 HEAPPROFILE="/home/hunter/log/test.log" ZSmartOCPro IN

说明:HEAPPROFILE指定生成dump文件的位置;HEAP_PROFILE_ALLOCATION_INTERVAL,程序内存每增长这一数值之后就dump 一次内存,默认是1G, 104857600=100M
内存dump文件
  • 4 利用pprof来分析内存文件

/usr/local/bin/pprof --text --base=/home/hunter/log/test.log.0003.heap /home/hunter/bin/ZSmartOCPro /home/hunter/log/test.log.0009.heap

内存差异

很快定位出问题所在,和猜想一直。业务代码写过程漏了调用vector的复位,导致一直增长。

修改问题后重新测试后,发现解决内存差异问题。

/home/hunter/log>/usr/local/bin/pprof --text --base=/home/hunter/log/test.log.0020.heap /home/hunter/bin/ZSmartOCPro /home/hunter/log/test.log.0055.heap
Using local file /home/hunter/bin/ZSmartOCPro.
Using local file /home/hunter/log/test.log.0055.heap.
Total: -0.0 MB
0.0 -272.7% -272.7% 0.0 -272.7% std::_Vector_base::_M_create_storage (inline)
0.0 -218.2% -490.9% 0.0 -218.2% Json::Value::Value
0.0 -109.1% -600.0% 0.0 -109.1% TariffTemplate::Instance (inline)
0.0 -109.1% -709.1% 0.0 -109.1% list_resize (inline)
0.0 -109.1% -818.2% 0.0 -109.1% std::vector::push_back (inline)
0.0 -4.5% -822.7% 0.0 -4.5% std::__cxx11::basic_string::_M_mutate
0.0 -4.5% -827.3% 0.0 -4.5% zmq::msg_t::init_size
0.0 -0.0% -827.3% -0.0 109.1% AcmManager::Initialize
0.0 -0.0% -827.3% 0.0 -109.1% AcmManager::RetrieveAccumulation
0.0 -0.0% -827.3% 0.0 -109.1% AcmManager::RetrieveDataFromTableAcm

至此,问题彻底解决。

小结

因为工具实现原理的差异,在解决问题时也各有特长:

  • Valgrind:适合解决非法读写,无主内存泄露等问题定位。
  • gperftools:长期运行进程内存使用量应该是稳定的,gperftools适合发现运行过程中内存差异,提供泄露的函数范围。而且对影响影响小,适合高压力快速复现。
  • getrusage:虽然对于定位问题帮助不大,但是可以用以守护进程监控管理业务进程的内存使用情况。
    以上是使用工具解决问题的过程,供学习和参考。感谢同事协助一起定位和解决。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容