论坛首页 Java企业应用论坛

又见内存泄露

浏览 21143 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-08-17  
SOA
1、首先是测试人员使用Loadrunner测试的过程中发现系统的吞吐率会随着时间而下降,在排除了测试数据分布不均的问题在测试,发现吞吐率保持稳定的一段时间后会陡然下降,平均事务处理时间陡然上升。于是,对系统的运行进行监控,在客户端压力平均的时候,系统内存两个小时内从500m上升到1G,基本上可以认定是内存泄露。
1.1系统的吞吐率图


1.2系统的平均事务处理时间图


2、添加 verbose:gc启动的参数,重新测试,发现每次Full GC后的对象空间持续缓慢增加,过了一段时间后,发现Miner GC无法释放空间,每次GC都是Full GC,到最后,Full GC也无法释放出空间出来。


3、安装netbean profile对系统使用情形进行监控,发现
a、堆内存的已使用空间缓慢增长,直到最大内存限制;


b、接近最大内存限制的时候,内存平均对象的年龄(Surviving Generations)急剧上升,见下图中的红线。
c、Relative Time Spendt in GC 直剧上升,分析,当GC占用的CPU大量时间,系统的吞吐率下降,和LoadRunner的测试结果是符合的。见下图中的蓝线



4、获取内存对象的静态映像,发现内存中占空间最大的几种存活对象的平均对象年龄都比较大,并且随着时间的增长,这几类对象占用的空间与平均对象年龄都不停的加大,见图。



5、查看这几种对象的分配时候的程序堆栈,发现这些对象是mule框架初始化组件对象的时候所创建,心中犯疑,什么促使mule框架不停的创建全局对象?

追踪了几条对象生成的路径,发现不断增长的对象似乎都是org.mule.providers.soap.xfire.transport.MuleLocalTransport产生的,例如,19m的HashMap$Entry[]由MuleLocalTransport的父类org.codehaus.xfire.transport.AbstractTransport构造的时候产生,17m的HashMap$Entry[]由MuleLocalTransport的createNewChannel的方法生成。每调用createNewChannel一次就会生成一个全局对象org.codehaus.xfire.transport.DefaultEndpoint。而MuleLocalTransport对象只有在初始化的时候才会生成全局对象。继续看代码发现可以在XFireServiceComponent的setDescriptor打印日志来确认mule是否不停创建全局对象。

6、在XFireServiceComponent的关键初始化方法setDescriptor中添加日志,编译、重新打包,替换原来的类。新的类每当setDescriptor被调用一次就打印一行日志。重新测试,发现每个一段时间setDescriptor就被调用一次。由于每调用setDescriptor一次,就会产生大量的全局对象,并且全局对象不被释放,导致堆内存的增长。

7、会让mule如此疯狂的原因是什么呢?详细查看mule的配置,逐步集中到对象池与线程池的配置: 发现对象池的配置maxActive=5,maxIdle=5 <pooling-profile maxactive="5" maxidle="5" exhaustedaction="GROW" maxwait="1000">,配置明显过小,在对象池的exhaustedAction="GROW" 。线程池的配置maxThreadsActive明显大于对象池的配置。这样mule肯定会创建对象,由于</pooling-profile> 对象池的对象很快由于超过了maxActive=5,多余的对象会被释放。
<pooling-profile maxactive="5" maxidle="5" exhaustedaction="GROW" maxwait="1000">
8、修改该配置maxActive=50,maxIdle=50,</pooling-profile> 对象池的exhaustedAction="Wait", <pooling-profile maxactive="5" maxidle="5" exhaustedaction="GROW" maxwait="1000">重新运行,</pooling-profile> maxThreadsActive=50 <pooling-profile maxactive="5" maxidle="5" exhaustedaction="GROW" maxwait="1000">。是运行了7个小时,内存正常。

9、由于</pooling-profile> 对象池的exhaustedAction="Wait"的情况下没有内存泄露,不等于说mule没有问题。mule放入对象池的对象是DefaultMuleProxy。为了与xfire集成,对象池放入一类特殊对象:
org.mule.impl.model.DefaultMuleProxy -〉org.mule.providers.soap.xfire.XFireServiceComponent
后者包含成员变量org.mule.providers.soap.xfire.transport.MuleLocalTransport与org.mule.providers.soap.xfire.transport.MuleUniversalTransport在对象池释放DefaultMuleProxy后没有得到释放。从netbeans profile的内存映像上看:这两个类的对象,生成对象的数目==存活的对象数目,一个都没有释放过。这两类对象又携带了大量的全局对象,导致内存泄露。

10、上述两类对象为什么无法释放?经过一番查找,发现在XFireServiceComponent的setDescriptor方法,对象被注册到org.codehaus.xfire.transport.DefaultTransportManager中去了,之后不见有unregister的操作。代码如下:
        getTransportManager().register(transport);
        getTransportManager().register(universalTransport);


   发表时间:2007-08-20  
又经过一轮测试,对之前的文章进行了修正。
0 请登录后投票
   发表时间:2007-08-24  
解释得挺详细,很有参考价值。
0 请登录后投票
   发表时间:2007-08-24  
一个完整的解决过程,非常棒
0 请登录后投票
   发表时间:2007-09-04  
哈哈,俺的东西终于有人看了:)
0 请登录后投票
   发表时间:2007-09-04  
工欲善其事 必先利其器呀
0 请登录后投票
   发表时间:2007-10-25  
这东西写得太刺激了。。。。
0 请登录后投票
   发表时间:2007-10-30  
想知道从4开始那些图是什么工具生成的
0 请登录后投票
   发表时间:2007-10-31  
很不错的文章,内存溢出是很头疼的问题,要解决它不仅需要掌握工具,了解JAVA内存机制,还要对编程比较了解。
我最近也在了解这些,4开始的图应该是NetBean Profiler的吧?
0 请登录后投票
   发表时间:2007-10-31  
恩,这些天也在做内存泄漏的工作,体会很深,支持版主
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics