中国开发网: 论坛: 程序员情感CBD: 贴子 167680
疯子张
java高人帮忙看一下。这是我公司请的专门解决宕机问题的人写的报告,看看他说得是否正确。
一、性能故障的现象报告

当登陆用户达到比较高的并发数量的时候,应用服务器的JVM使用的内存逐渐上升,最终达到了允许的最大值2GB,同时应用服务器的CPU占用率达到了100%,不再响应页面访问请求。

通过应用服务器的系统日志和应用程序的日志来分析,出现故障的时候系统的状态:

1、EJB事务阻塞,无法完成事务提交,超时后事务失败

[05-7-20 9:19:33:765 GMT+08:00] ca86fd TimeoutManage I WTRN0006W: 360 秒后事务 57415344:0000000000000bc600000001e19ba92a69ab72c1d79a2e3570eb0d6e2c1d6bbe73657276657231[] 已超时。
[05-7-20 9:19:33:850 GMT+08:00] 1a89f49 TimeoutManage I WTRN0006W: 360 秒后事务 57415344:0000000000000bcf00000001e19ba92a69ab72c1d79a2e3570eb0d6e2c1d6bbe73657276657231[] 已超时。
[05-7-20 9:20:16:512 GMT+08:00] 1a89f49 TimeoutManage I WTRN0006W: 360 秒后事务 57415344:0000000000000beb00000001e19ba92a69ab72c1d79a2e3570eb0d6e2c1d6bbe73657276657231[] 已超时。
……
[05-7-20 9:20:16:535 GMT+08:00] 1a89f49 TimeoutManage I WTRN0006W: 360 秒后事务 57415344:0000000000000bed00000001e19ba92a69ab72c1d79a2e3570eb0d6e2c1d6bbe73657276657231[] 已超时。
[05-7-20 9:20:16:685 GMT+08:00] 6f29c8 TimeoutManage I WTRN0006W: 360 秒后事务 57415344:0000000000000bf200000001e19ba92a69ab72c1d79a2e3570eb0d6e2c1d6bbe73657276657231[] 已超时。
[05-7-20 9:20:16:703 GMT+08:00] 1f40b69 TimeoutManage I WTRN0006W: 360 秒后事务 57415344:0000000000000bf300000001e19ba92a69ab72c1d79a2e3570eb0d6e2c1d6bbe73657276657231[] 已超时。

2、JVM内存已满,线程被挂起

[05-7-20 9:23:20:400 GMT+08:00] 1f40b69 ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 98”(1397831)已活动了 639,238 毫秒,可能被挂起。服务器中可能被挂起总计 1 个线程。
[05-7-20 9:23:20:889 GMT+08:00] 1f40b69 ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 76”(883497)已活动了 639,300 毫秒,可能被挂起。服务器中可能被挂起总计 2 个线程。
[05-7-20 9:23:20:923 GMT+08:00] 1f40b69 ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 132”(6f9db5)已活动了 610,353 毫秒,可能被挂起。服务器中可能被挂起总计 3 个线程。
[05-7-20 9:23:20:941 GMT+08:00] 1f40b69 ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 101”(2006d5)已活动了 630,767 毫秒,可能被挂起。服务器中可能被挂起总计 4 个线程。
[05-7-20 9:23:20:963 GMT+08:00] 1f40b69 ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 87”(a83492)已活动了 639,240 毫秒,可能被挂起。服务器中可能被挂起总计 5 个线程。
……
[05-7-20 9:27:52:733 GMT+08:00] ca86fd ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 168”(1ec0f49)已活动了 816,312 毫秒,可能被挂起。服务器中可能被挂起总计 110 个线程。
[05-7-20 9:27:52:793 GMT+08:00] ca86fd ThreadMonitor W WSVR0605W: 线程“Servlet.Engine.Transports : 99”(1ddb2)已活动了 837,544 毫秒,可能被挂起。服务器中可能被挂起总计 111 个线程。


二、现象分析

随着应用程序同时操作的用户增多,JVM内存占用逐渐增高,垃圾收集器的内存收集跟不上内存增长的速度,最后导致内存耗尽,应用服务器不再响应。当发生该现象的时候,同时操作该系统的用户数量不超过100个,按照应用服务器的负载能力和正常的应用程序估算,不应该占用多达2GB的内存。根据对应用系统的深入分析,可能导致性能故障的原因,按照可能性如下:

1、EJB的事务配置失误

在当前的生产系统上,所有的EJB的所有的方法的事务类型统统设置为:RequiresNew类型,该类型声明每个EJB的每个方法操作都需要启动一个新的容器全局事务来处理当前的操作。要知道,EJB容器的全局容器事务资源是非常昂贵的服务器资源,会占用很多的内存,关联很多相关的上下文内存堆栈,关联数据库资源。如果在很高的并发访问情况下使用RequiresNew事务类型,会导致应用服务器的全局容器事务数量保持在一个非常高的水平,从而导致内存巨大消耗和资源的锁定,并且一旦全局容器事务的资源被锁定,那么这些资源关联的上下文内存堆栈就无法被及时释放,那么内存就会持续攀升。从当前的服务器日志来判断,在系统宕机之前,报告了很多事务超时失败的错误,这也说明,在系统宕机之前,已经有相当多的事务被锁定。

此外,有些EJB的只读查询方法会执行非常复杂的关联SQL语句,查询结果集非常庞大,一旦对这些非常耗时的只读查询方法声明全局容器事务,必然会造成全局容器事务非常耗时,从监控工具的报告来看,复杂查询的事务往往持续了几十秒,对内存的消耗非常庞大。

另外,一旦声明事务的EJB方法执行失败(例如出现SQLException),则全局容器事务需要回滚,而全局容器事务的回滚期间也会进行资源的锁定,消耗比较多的内存。因此在当前的情况下,尽量减少全局容器事务的使用,降低事务回滚发生的概率,会对提高系统性能,降低JVM内存起到非常显著的作用。

根据我的判断,EJB事务配置事务是导致内存溢出,系统宕机的最大可能原因!

解决方案:

将只读查询的EJB方法声明为NotSupported类型,将数据修改的EJB方法的事务声明为Required类型,尽最大可能减少全局容器的使用。


2、数据库连接池设置失误

当前生产机器数据库连接池最大允许连接仅仅设置为30个!一旦并发的请求超过30个,或者数据库查询非常耗时,那么就会立刻导致数据库连接不够用,那么后续的访问请求就会统统挂起等待空闲的数据库连接池。由于当前软件系统是在EJB里面访问数据库,那么一旦登陆系统的用户达到很高的数量,那些访问数据库的EJB线程统统被挂起等待,并且这些EJB的全局容器事务也被统统挂起等待,而调用这些EJB的Servlet线程也统统挂起等待。其结果就是,一旦并发访问的用户数量比较多的时候,数据库连接池就满了,接下来所有的Servlet线程,EJB容器事务,EJB实例统统挂起,最后导致JVM内存溢出,系统宕机。

数据库连接池设置数量过小,也是导致性能故障最重要的原因

解决方案:

在应用服务器上面修改数据库连接池最大数量为100或者更多。


3、PurchaseManagementBean的问题

从当前的日志分析,系统日志抛错基本上都是调用PurchaseManagementBean的时候发生的。根据最近两天使用Loadrunner对系统进行压力测试的结果表明,PurchaseManagementBean也是系统负载比较高的地方。我浏览了一遍PurchaseManagementBean和Bean调用的DAO方法源代码,包含了非常多的复杂的SQL关联查询和某些循环插入数据的操作。如果对这个EJB以及相关DAO源代码进行仔细的代码优化,则可以提高明显提高系统的性能。


4、EJB查询方法返回的结果集比较庞大

当前很多EJB查询方法返回的结果集比较庞大,这些结果集要通过序列化之后传给Servlet的Action,Action拿到结果集再反序列化,才能使用。如果并发访问的用户非常多,而查询的结果集又非常庞大,就会导致JVM的内存占用达到一个非常高的水平。

解决方案:

仔细优化EJB调用的复杂查询DAO方法,减少不必要的数据传送。


三、故障总结和解决方案

根据这两天的分析,我的结论是:

导致性能故障最大可能性就是EJB事务配置失误和数据库连接池配置失误

解决方案:

1、开发小组去修改EJB的配置文件,查询方法声明为NotSupported,不启动事务;修改数据方法声明为Required,不启动新的事务

2、现场部署修改Websphere配置,将连接池数量扩大到100以上

3、开发小组请仔细排查PurchaseManagementBean调用的相关DAO查询方法,特别是复杂的SQL关联查询,和某些在循环体内插入或者更新数据的操作,进行必要的优化。
几年前,技术抛弃了我;现在,我抛弃了技术。


相关信息:


欢迎光临本社区,您还没有登录,不能发贴子。请在 这里登录