2007-08-17
又见内存泄露
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,配置明显过小,在对象池的exhaustedAction="GROW" 。线程池的配置maxThreadsActive明显大于对象池的配置。这样mule肯定会创建对象,由于
对象池的对象很快由于超过了maxActive=5,多余的对象会被释放。
8、修改该配置maxActive=50,maxIdle=50,
对象池的exhaustedAction="Wait",
重新运行,
maxThreadsActive=50
。是运行了7个小时,内存正常。
9、由于
对象池的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);
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
8、修改该配置maxActive=50,maxIdle=50,
9、由于
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);
评论
lean1252
2008-06-28
说的很好,以后有个解决问题的参考了:)
zpple
2008-04-28
很是精髓,但是没有做运维,还不是很懂。
galaxystar
2007-10-31
请问LZ的app 应用环境是?
abcdzt
2007-10-31
恩,这些天也在做内存泄漏的工作,体会很深,支持版主
caisd
2007-10-31
很不错的文章,内存溢出是很头疼的问题,要解决它不仅需要掌握工具,了解JAVA内存机制,还要对编程比较了解。
我最近也在了解这些,4开始的图应该是NetBean Profiler的吧?
我最近也在了解这些,4开始的图应该是NetBean Profiler的吧?
javaeyes
2007-10-30
想知道从4开始那些图是什么工具生成的
likeblood
2007-10-25
这东西写得太刺激了。。。。
bluemeteor
2007-09-04
工欲善其事 必先利其器呀
shallon
2007-09-04
哈哈,俺的东西终于有人看了:)
dennis_zane
2007-08-24
一个完整的解决过程,非常棒
Lucas Lee
2007-08-24
解释得挺详细,很有参考价值。
shallon
2007-08-20
又经过一轮测试,对之前的文章进行了修正。
发表评论
提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则
- 浏览: 19066 次
- 性别:

- 来自: 0

- 详细资料
搜索本博客
我的相册
doc
共 20 张
共 20 张
最近加入圈子
最新评论
-
又见内存泄露
说的很好,以后有个解决问题的参考了:)
-- by lean1252 -
一种SSO的实现方案
登录信息的管理中心一旦建立,就会造成所有的系统都对这个管理中心产生依赖。如果这个 ...
-- by downpour -
一种SSO的实现方案
有很多所谓的登陆的话,就是从统一登录系统中想办法把用户信息传递到另外的系统。这似 ...
-- by 香克斯 -
又见内存泄露
很是精髓,但是没有做运维,还不是很懂。
-- by zpple -
SAAS在电子商务中的应用分 ...
淘宝模式不更好?
-- by rtdb






评论排行榜