人的差别在于业余时间,而一个人的命运决定于晚上8点到10点之间。 北京安全测试精英QQ群:164265622 北京白盒测试精英QQ群:164265999 北京性能测试精英QQ群:164266156 北京自动化测试精英群:212723528 北京软件测试精英QQ群:86920845

内存泄漏的检测、定位和解决经验总结(二)

上一篇 / 下一篇  2011-09-02 13:27:13 / 个人分类:性能测试

2.3诊断过程

采用BoundsChecker检测和Rational Purify工具不一样,Rational Purify可以在程序运行中随时给出即时的内存泄漏报告,而使用BoundsChecker则只有在程序自行结束后才会触发它去收集数据给出内存泄漏报告(注意:必须是程序自己运行完毕退出,而不能人为的去关闭该程序否则得不到内存泄漏报告)

2.3.1对程序的改造

由于要让程序自动结束,所以要对程序结构进行改造,前段时间部门推行的单元测试为此次内存泄漏检测提供了很好的基础。

CSS模块为单元测试专门创建了一个新的VC Project,共用CSS中的所有代码,但自己工程中的单元测试代码又不会影响CSS原来的代码。此次进行内存泄漏的检测就首先打算运行单元测试的代码然后检测是否有内存泄漏。

单元测试程序改造如下:

1)程序启动时开启两个线程,一个CSS线程负责正常启动CSS,另一个测试线程负责运行测试用例对已经正常启动的CSS进行测试。

2)测试线程运行测试用例完毕后要让CSS线程退出,并结束自己,只有这样BoundsChecker才能拿到内存泄漏数据。

2.3.2内存泄漏检测

第一次内存泄漏检测,CSS测试程序运行一遍测试用例正常退出后,得到BoundsChecker的检测报告显示:

l        所有采取单态模式的类中的相应的指针指向的动态分配的对象都没有被释放。

l        线程被异常终止时,该线程中动态分配的内存没有释放掉。

l        动态分配的全局变量(对象)没有被释放掉。

l        绝大部分的内存泄漏集中在ACE/TAO(ACECORBA)提供的一些函数里。

其中前三种情况就是1.问题描述中提到的在第一次内存泄漏检测就解决的,最后一个是造成内存泄漏问题严重的罪魁祸首,在第二次内存泄漏检测才彻底解决,这里列下相应的解决方法和思路。

2.3.3内存泄漏修正方法

BoundsChecker Data Collection设置中将跟踪的call stack深度加大即可以晰的看到每个内存泄漏是从哪个函数哪条语句产生的。BoundsChecker出的报告中内存泄漏数据和资源泄漏数据对于查找内存泄漏问题都是非常有帮助的。[见附录23]

根据报告进行内存泄漏定位发现泄漏分为两种:

1)在程序关闭时候的内存泄漏,只有程序关闭时才发生的内存泄漏,如一些动态分配的全局对象,线程没有正常退出而是直接被Terminate掉等。

2)程序运行中的内存泄漏,即运行中会不断发生泄漏,如一段代码动态分配了内存忘释放了,这样这段代码每次运行都会产生内存泄漏。

 

2.3.3.1程序关闭时内存泄漏检测和修正方法

虽然程序关闭时才发生的内存泄漏不会导致程序运行中内存使用的增长,但由于BoundsChecker给出的报告中只给出程序运行期间发生的所有内存泄漏,所有关闭时内存泄漏和运行时内存泄漏都混杂在一起不好定位,为此首先要解决关闭时的内存泄漏问题,由于关闭时内存泄漏一般都是动态分配的全局变量没释放、线程不是正常退出等情况产生的所以比较好定位。解决方法如下:

1)所有动态分配的全局变量在程序退出时都Delete掉。

2)让所有线程都正常运行结束退出,而不是被异常Terminate掉。

3)所有使用单态模式的类都必须有专门的内存清理函数在程序结束前调用。

经过以上方法的处理后,CSS程序剩下的内存泄漏全部集中在ACE/TAO(ACECORBA)提供的dll上。

2.3.3.2程序运行时内存泄漏检测和修正方法

第二次内存泄漏检测运行CSS测试程序得到的检测报告发现的内存泄露全部集中在ACE/TAO(ACECORBA)提供的dll上,原因可能有如下两种:

1) 程序中使用ACETAO的函数不当没有正确释放内存。

2) ACETAO库确实是存在内存泄露问题。

由于此时显示内存泄漏都是在TAO的函数中发生的,一下子没了思路不知道怎么查了,只好一步一步的去查程序代码了。

一开始测试程序只是将所有测试用例执行一遍然后就退出,为了测试是否会有运行时内存泄漏,让测试程序将所有测试用例运行多遍才退出,理论上若有运行时内存泄漏的话,运行n遍测试用例产生的内存泄漏应该是运行一遍产生的内存泄漏的n倍。照着这个思路往下走,发现实际上并不是这么回事,基本上内存泄漏的字节数不变,由此可见测试用例没有覆盖到真正产生运行时内存泄漏的代码,目前报告中发现的内存泄漏都只是程序关闭时产生的内存泄漏。

怎样定位哪段代码会产生运行时内存泄漏呢?程序运行时内存泄漏问题比较难定位,特别是程序有多个线程时,CSS内存泄漏检测首先采用的方法为进行线程隔离。

1)     线程隔离法

将程序中主要的可能产生内存泄漏的线程一个个进行隔离,每次只运行其中的一个或关闭其中的一个,然后看BoundsChekcer产生的内存泄漏字节数是否有变化,以此来判断该线程是否会产生内存泄漏。若该线程会产生内存泄漏则对该线程的代码进行仔细分析以找出原因。

CSS程序进行线程隔离后也发现这些线程都不产生内存泄漏。

2)     功能检查法

此时只好从CSS的功能上来定位了,CSS主要是和Web进行交互,接受Web发过来的指令然后进行开关会等操作。分析由于测试程序只是测试CSS的内部流程,而没有涉及到WebCSS的接口那块,而WebCSS之间是使用CORBA机制通讯的,恰好内存泄漏报告也显示内存泄漏基本集中在TAO(CORBA开发库)那部分。

于是选择一个Web页面(获取mcu列表),不断的刷新该页面,发现此时CSS使用内存果然在不断上涨。也就是所获取mcu列表这个功能对应的代码会产生内存泄漏。

由于这个小功能对应的代码是有限的就几十行,仔细的检查也没发现什么问题,此时想到测试部认为NMS模块没有内存泄漏,只要对比NMS模块的类似功能的代码就能知道内存泄漏的原因了。但检查发现两边代码是一样的,然后去测NMS的相关页面发现NMS也有内存泄漏,只是因为NMS业务量比较小不太容易暴露该问题而已。

此时更相信可能是TAO开发库自己产生的内存泄漏了,正要放弃之际,慕然出现了转机,我们科室的李喜欣也使用了TAO库,此时彭峰和他也参与进内存泄漏的攻关,经检测发现喜欣的程序没有内存泄漏。

一开始认为是使用的TAO库的版本不一样,于是就将我们的代码放入李喜欣TAO库所在环境中测试,发现在他哪里还是也有内存泄漏,也就是所和TAO库的版本没有关系。可能确实是代码中函数使用方法有误。

于是针对那部分功能代码开始一条条语句屏蔽以期找出产生内存泄漏的语句,功夫不负有心人当我们将一条memset()语句屏蔽掉后终于没有内存泄漏了。

分析原因是由于memset将一个CORBA对象所占内存清零了,虽然IDL中定义的为一个结构,但经过IDL文件的编译,实际上在CORBA已将IDL中的结构转换为相应的CORBA对象了,该对象中含有智能指针,这些指针在对象创建时都已经指向了一些动态分配的空间,由于memset操作,导致这些指针全部清零,于是那些指针原来指向动态分配的空间就永远都得不到释放进而产生了内存泄漏。

修正memset问题后就彻底的解决了内存泄漏问题。

三、推广建议

本文简要介绍了进行内存泄漏检测的一些工具,并提出了一些进行内存泄漏检测的有效方法和经验,对于要进行内存泄漏检测的项目具有一定的借鉴意义。

若需要文中提到的一些内存检测工具可以联系作者。

四、后记

经过漫长的内存泄漏检测终于将内存泄漏问题解决了,修正CSSNMS的内存一直都稳定在13M左右,在查找内存泄漏的问题中有以下几点体会:

1.      不要随便怀疑别人首先要怀疑自己,第一次内存泄漏检测没有彻底找出原因就是因为认为是开发库自己的内存泄漏,第二次也差点就打算收工了。最后发现还是自己使用不当导致的。

2.      要有耐心,不能烦躁,当一个问题你找不到一丝丝线索时多和别人交流是很有帮助的,这次能彻底解决内存泄漏就是由于彭峰和李喜欣的加入,三个丑皮匠顶个诸葛亮真是至理名言。

3.  


TAG: 内存泄漏检测

 

评分:0

我来说两句

Open Toolbar