一、故障或失误概况
  局端MCU项目中CSS(Conference Schedule System)、NMS(NetWork Management System)模块自2.03版本起有内存泄露的问题,开发NGN版本时也花过大量的精力来争取解决这个问题,虽然也修正了一些内存泄漏,但终检测工具表面现象显示剩下的内存泄露都是所使用的开发库的代码产生的,于是也大意的认为是所使用的ACE/TAO库本身有内存泄漏,于是无果而终,使这两个模块的内存泄漏问题一直延续到2.03.20x版本。
  由于后续测试部和开发部进行测试时引入了Robot来进行自动测试,使业务操作量巨升,此时CSS模块的内存泄漏问题更明显了,从程序启动时的13M内存,经过一两个月后可以飙升到200 M左右,正因为此局端MCU项目好几个程序都采用了看门狗的方式来定时检测程序的状态防止程序当掉。
  由于问题比较严重,于是再次进行内存泄漏问题的攻关,测试时NMS模块业务操作量小内存泄漏不明显,于是此次攻关重点是查CSS的内存泄漏问题。
  此次再次进行CSS行内存泄露问题的研究,力争解决CSS的内存泄露问题,实在找不到解决方案(例如为所使用的开发库的原因)也定位出具体原因供项目组参考。
  二、诊断过程
  2.1工具介绍
  目前Windows平台上流行的内存泄露检测工具有Rational Purify、BoundsChecker、insure++,由于CSS较复杂又是多线程采用Rational Purify工具程序启动不起来,无法进行检测;parasoft公司的insure++工具传说中比较好用,但由于我们公司没有相应的licence无法使用;后选择采用限制版的BoundsChecker来进行检测。
  还有是要一个好的实时检测程序使用内存状况并能实时记录下每个时刻内存使用情况的工具,我们这里使用的是本部传输产品测试部自己开发的一个小工具MemSample。(见附录1)
  对于工具方面这也是个问题,若每个公司都有个统一可用工具列表,然后附上相应的使用说明可以省了许多精力和时间。现在好多工具都是自己去浩如烟海的网上找,有些还需要licence等,这是个很大的精力浪费。
  2.2诊断概况
  CSS模块为后台服务程序通过访问Web页面的形式来对外提供服务,运行了该模块的程序过一段时间后机器性能明显下降,经检测为内存严重消耗。测试部使用内存监视工具MemSample对各模块的内存进行监测,发现在一个月内CSS程序占用的内存从初始的13M飙升到200多M。
  由现象可以判断肯定是存在内存泄漏问题,由于CSS对外提供服务接口繁多一时不知道那个接口导致的内存泄漏,因此确定如下测试过程:
  测试方法1:通过单元测试的方法来调用部分接口进行测试。
  采用CPPUNIT测试框架辅助以桩程序的方式来模拟Web页面手工操作的方式来调用相应接口进行测试。
  程序测试过程中采用内存泄漏代码级检测工具BoundsChecker全程跟踪程序中内存分配和释放情况。
  采用内存监测工具MemSample对程序占用的整体内存进行监测并采集各时间段内存占用数据。
  经过第一种测试方法发现了程序中存在的一些内存泄漏,并解决之,但解决之后发现还是存在内存泄漏,通过分析发现模拟测试步骤和真实环境下的操作的不同点是:
  模拟测试是在程序内部直接调用接口,而真实环境下是通过访问Web页面,Web页面再通过CORBA协议来调用程序接口。即模拟测试少了CORBA调用那部分的操作,为此重新制定第二种测试方法:
  测试方法2:真实操作环境下进行测试
  l由于接口繁多,重点挑一两个使用频繁的接口进行测试。
  l人工访问调用所测接口的那个Web页面,不断频繁刷新,保证和真实操作环境完全一致。
  l测试过程中启动内存监测工具MemSample对CSS占用内存进行监测并采集各时间段内存占用数据。
  测试后发现所测接口均存在内存泄漏,每次接口调用都会有几十K的内存泄漏,此时扩展到其它接口发现所有使用CORBA协议通过Web页面调用的接口均存在内存泄漏。比较两种测试方法可以初步确定是在调用CORBA协议部分的代码出现了内存泄漏,下面进行整个检测过程详细描述。
  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的检测报告显示:
  所有采取单态模式的类中的相应的指针指向的动态分配的对象都没有被释放。
  线程被异常终止时,该线程中动态分配的内存没有释放掉。
  动态分配的全局变量(对象)没有被释放掉。
  绝大部分的内存泄漏集中在ACE/TAO(ACE的CORBA库)提供的一些函数里。
  其中前三种情况是1.问题描述中提到的在第一次内存泄漏检测解决的,后一个是造成内存泄漏问题严重的罪魁祸首,在第二次内存泄漏检测才彻底解决,这里列下相应的解决方法和思路。
  2.3.3内存泄漏修正方法
  BoundsChecker Data Collection设置中将跟踪的call stack深度加大即可以晰的看到每个内存泄漏是从哪个函数哪条语句产生的。BoundsChecker出的报告中内存泄漏数据和资源泄漏数据对于查找内存泄漏问题都是非常有帮助的。[见附录2、3]
  根据报告进行内存泄漏定位发现泄漏分为两种:
  1)在程序关闭时候的内存泄漏,只有程序关闭时才发生的内存泄漏,如一些动态分配的全局对象,线程没有正常退出而是直接被Terminate掉等。
  2)程序运行中的内存泄漏,即运行中会不断发生泄漏,如一段代码动态分配了内存忘释放了,这样这段代码每次运行都会产生内存泄漏。
  2.3.3.1程序关闭时内存泄漏检测和修正方法
  虽然程序关闭时才发生的内存泄漏不会导致程序运行中内存使用的增长,但由于BoundsChecker给出的报告中只给出程序运行期间发生的所有内存泄漏,所有关闭时内存泄漏和运行时内存泄漏都混杂在一起不好定位,为此首先要解决关闭时的内存泄漏问题,由于关闭时内存泄漏一般都是动态分配的全局变量没释放、线程不是正常退出等情况产生的所以比较好定位。解决方法如下:
  1)所有动态分配的全局变量在程序退出时都Delete掉。
  2)让所有线程都正常运行结束退出,而不是被异常Terminate掉。
  3)所有使用单态模式的类都必须有专门的内存清理函数在程序结束前调用。
  经过以上方法的处理后,CSS程序剩下的内存泄漏全部集中在ACE/TAO(ACE的CORBA库)提供的dll上。