赞
踩
今天向大家分享一个工作中,内存泄漏问题的解决流程及其思路。相信对刚接触这一块的朋友,有借鉴意义。
最近XX项目中,XX厂商反馈我们的XX程序在指定情况下,会产生内存泄漏,随着时间的增长,造成OOM错误。该问题本由AA同事处理,但由于我比较感兴趣,就一同分析并最终解决。虽然最终的解决方式比较简单,但是问题分析的流程,我觉得还是比较具有参考意义的。在这里向大家分享这个内存泄漏问题从出现到解决的流程。
XX厂商同事反馈:当APA与车机的连接线断开时,我们的XX程序会随着运行时间的延长,其堆空间占用会越来越大。最终导致OOM错误。
判断堆空间增加的方式为:cat /proc/[pid]/smaps
当我从AA同事这里得到上述的信息时,我脑海中,已经冒出了两个问题:
APA与车机的连接线断开,这是问题出现的条件。根据XX程序的代码逻辑,若有内存泄漏,应该是不断尝试ups_init导致的。(确定了内存泄漏代码方向)
cat /proc/[pid]/smaps
,这是XX厂商判断内存泄漏的依据。因为,我并不了解smaps文件属性,所以我第一反应是持有怀疑态度的(他们的这个判断依据可能不正确)。
根据上面两个问题,就决定了我接下来的分析思路:
了解smaps 文件属性,该判断依据是否可靠。
使用valgrind 工具判断uc-slave的内存泄漏点。(其实直接走这一步就可以,主要我也想学习一下smaps文件属性)
在分析smaps 文件属性之前,我先向大家介绍一下两个概念:虚拟内存,驻留内存。
关于这两个概念,我借用一个大佬的图分享一下。
图一:虚拟内存空间到物理内存空间的映射
虚拟内存:就是假象的内存空间,程序中变量,函数的地址,其实都是虚拟内存空间的地址。但是程序运行时,会将虚拟内存中需要被访问的部分映射到物理内存中(暂时不会用到的应该在磁盘)。
问题:请问暂时用不到的代码段,数据段,存储在哪呢?若是在磁盘中,linux 系统中当程序运行后,再将可执行文件删除,为什么不影响呢?
答案我就不在这描述,希望大家可以去自己研究一下,有答案的朋友可以在评论区进行回答。
驻留内存:就是那些被映射到进程虚拟内存空间的物理内存。
图一中,物理内存中被着色的就是驻留内存。比如A1,A2,A3,A4,是A进程的驻留内存;B1,B2,B3,是B进程的驻留内存。我们一般所说的进程占用多少内存,就是指进程的驻留内存。
问题:其中A4与B3在一个物理内存中,表示两个进程的虚拟内存映射到同一块物理内存,这种情况合理吗?
答案是合理的,这种情况称为共享内存,表示这块内存是可以被多个进程访问的。比如,我们可以采用共享内存的方式,进行多进程通信;现在大部分的进程也会依赖一些动态库,比如libc.so,libld.so等。这些库仅会在物理内存中保留一份,达到节省内存的目的。这也是动态库相比较于静态库的优势。
smaps文件是描述了进程的内存消耗情况。文件内容格式如下:
图二:smaps 文件格式
简单描述文件中各字段的属性:
其中第一行从左到右依次表示:虚拟空间的地址范围 权限表示 映射文件偏移 设备号 inode 文件路径
Size: 表示该虚拟空间的范围大小
Rss:表示该映射区域当前在物理内存中占用的空间
Pss:表示共享内存中平摊的物理内存。
Shared_Clean:和其它进程共享的未修改的page的大小
Shared_Dirty:和其它进程共享的被改写的page的大小
Private_Clean:未被改写的私有页面的大小
Private_Dirty:已被改写的私有页面的大小
而客户反应的问题是:随着程序运行时间增长,堆空间占用的内存会越来越大。如下图:
图二:客户反馈堆空间一直增加
综上所述,我认为客户的判断依据是正确的,并且是非常好的方式。
既然已经确认存在内存泄漏问题,那么接下来就是解决内存泄漏了。关于定位内存泄漏问题,我们肯定会想到valgrind工具。很可惜,客户平台上并没有valgrind 工具,那么就需要我们进行工具移植了。开源工具的移植可参考我的另一篇文章《开源工具移植(gdb)》。
而valgrind的使用方式,可参考我的博客《快速定位内存泄漏的套路》。
一个小时过去了~~~
通过上面的移植及调试步骤,得到了下面的两个log信息文件。
图三:valgrind 信息对比
左图是程序启动后,内存消耗情况,有164个内存消耗记录;右图是程序运行半小时,内存消耗情况,有204个内存消耗记录。
从图中可知,随着运行时间增加的内存消耗,主要集中在still reachable中(表示这部分的内存还是有机会被释放的,说明指向该部分内存的地址是有保存的,猜测是代码中没有释放)。
再详细对比两个文件中内存消耗的记录,发现多余的40条记录。都是由MQTTAsync_createWithOptions接口导致的,因此,基本可以确认是由该接口进行malloc申请内存的。
MQTTAsync_createWithOptions接口申请内存接口如下:
图四: 代码分析
分析:
本篇结合工作实例,从问题出现的背景,思路,分析流程进行描述。其中涉及的知识点也较多。对解决内存泄漏相关问题具有较高的参考意义。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。