J2EE性能问题的分析

摘 要:诊断复杂的J2EE应用程序中的性能问题是一项艰巨任务。IT部门人员的责任是确保应用程序具备优越的性能且持续可用。出现性能问题时能够准确判断应归咎于特定应用程序组件,还是某个应用程序连接的后台或前端系统。本文着重讨论J2EE应用程序中常见的性能问题;分析引起这些性能问题的根本原因;提出如何准确诊断并迅速排除性能问题的可行性建议,以供参考。

前言

      如果需要您来诊断J2EE应用程序中的性能问题,Java系统的复杂性俨然将问题变为难以诊治的疑难杂症。为了准确查明问题所在,您需要对系统的故障征兆有一个全面的了解,还需要做大量的调研工作,提出适当的补救措施。本文着重讨论J2EE应用程序中常见的性能问题,分析引起这些性能问题的根本原因,提出如何准确诊断并迅速排除性能问题的可行性建议,以供参考。

故障征兆

      应用程序出现性能问题的征兆是什么?您所观察到的故障征兆诱导你对所有可能出现问题进行全面检索。拿起笔记本开始向人们收集数据。努力从假定推断中抽身出来,以确凿的证据认定系统的实际行为,查找引起性能问题的根本原因。系统中常见的故障征兆列表如下

      ■ 持续运行缓慢。时常发现应用程序运行缓慢。通过改变环境因子(如负载量、数据库连接数等)也无法有效提升整体响应时间。

      ■ 系统性能随时间的增加逐渐下降。在负载稳定的情况下,系统运行时间越长速度越慢。可能是由于超出某个阈值范围,系统运行频繁出错从而导致系统死锁或崩溃。

      ■ 系统性能随负载的增加逐渐下降。随着用户数目的增多,应用程序的运行越发缓慢。若干个用户退出系统后,应用程序便能够恢复正常运行状态。

      ■ 间发性的系统挂起或异常错误。有时候可能受负载或其它因素影响,当面不能完全显示,又或者是追踪到异常和堆栈的错误页,用户此时会看到系统挂起的情况。系统挂起的次数可能会稍有不同,然而即便是经过预烧(“burn in”)试验也无法完全消除。

      ■ 可预见的死锁。系统挂起或系统出错发生的频率随着时间的推移明显增多,直到系统完全死锁。通常借助自动重启的故障管理模式解决上述问题。

      ■ 系统突然出现混乱。系统正常运行,且在或多或少的一段时间内(譬如,可以是1小时也可以是3天)拥有一致优越的运行性能,却突然毫无征兆地出错甚至死锁。

 

性能问题的诊断为何如此困难

      特定使用模式下应用程序的性能优劣并无现成规则可循。(就严格意义上的实时工程而言,借助速率单调分析等方法的确可以完成这项工作,但已超出本文研究的范畴,在此不做讨论)。网络中是否存在另一个系统正在密集使用共享的后台服务,必将极大地影响系统的实际性能。系统性能或许还取决于JDBC驱动程序与数据库版本是否严格匹配。也可能出现这样的情况:开发人员三年前编写的代码,恰巧在这个时候出现特定类型的异常,而您急需的用以解决问题的回馈信息偏偏包含在那些异常当中。

      典型业务系统的性能呈现整体涌现性(emergent property)特征,是成千上万个变量(交互式)和决策共同作用的结果。就如同人的身体,是一个由众多联锁的组成部分和过程构成的整体。在文中我们采用统摄性模式以简化问题。

 

故障现象

      您所观察到的“疾病”征兆的根本原因是什么?是普通流感还是肺炎初期?是应用程序内部潜在的性能问题还是JVM外部进程出现了问题?下表是应用程序性能下降的常见原因列表,具体如下

 

      

“疾病” 描述 征兆 原因或解决方法
内存泄漏呈线性增长 各单元(如每个事务或每个用户等)的内存泄漏,导致内存使用率随时间或负载的增加呈线性增长。系统性能随时间或负载的增加大幅下降,重启后系统可恢复正常 系统性能随时间的增加逐渐下降, 系统性能随负载的增加逐渐下降 虽然有许多外在因素存在(如各单元数据的链表存储、尚未回收的缓冲中的回收或增长操作等),但最为常见的原因还是资源泄漏
内存泄漏呈指数级增长 内存泄漏呈双倍增长,导致系统内存消耗随时间呈指数曲线变化 系统性能随时间的增加逐渐下降,系统性能随负载的增加逐渐下降 虽然有许多外在因素存在(如各单元数据的链表存储、尚未回收的缓冲中的回收或增长操作等),但最为常见的原因还是资源泄漏
导致无限循环的编码缺陷 线程在while语句返回值为真的情况下发生阻塞, 将最终演变成为CPU密集型和等待密集型或螺旋等待变量 可预见的死锁 需要进行侵入式循环切除
资源泄漏 JDBC语句、CICS事务网关连接等出现资源泄漏,引发Java桥接层和后台系统出现严重性能问题 系统性能随时间的增加逐渐下降, 可预见的死锁, 系统突然出现混乱 通常是由于遗漏了Finally模块,或者只是没有用close函数关闭代表外部资源的对象
外部瓶颈 后台或其它外部系统(如用户验证)运行缓慢,大大影响J2EE应用服务器及应用程序的运行速度 系统持续缓慢运行, 系统性能随负载的增加逐渐下降 向专家(包括可靠的第三方或系统管理员等)咨询特定外部瓶颈问题的有效解决方法
外部系统的过度使用 J2EE应用程序发送的请求过大过多,滥用后台系统资源 系统持续缓慢运行, 系统性能随负载的增加逐渐下降 消除冗余的工作请求,分批处理同类工作请求,把大请求细分为若干个小请求,调整工作请求或后台系统(例如为公共查询的关键字建立索引)等
频繁调用CPU密集型组件的编码缺陷 J2EE领域的通病是:些许编码缺陷或少量编码的交互失败,都会令CPU挂起,从而将数据流量速度降至蜗行 系统持续缓慢运行, 系统性能随负载的增加逐渐下降 最好的解决方法是将数据存储在本地缓存中,或者为执行算法配备高速缓冲存储器
桥接层本身存在的问题 桥接层(如JDBC驱动、CORBA到遗留系统的连接等)存在执行缺陷。需要不断对数据和请求作编组和取消编组操作,导致该层的数据流量速度降至蜗行。这种故障现象在早期阶段与外部瓶颈极为相似 系统持续缓慢运行, 系统性能随负载的增加逐渐下降 检查桥接层与外部系统的版本是否兼容。如果可能的话,对不同桥接供应商进行评估。通过重新规划设计系统架构,则可完全旁路桥接层
内部资源瓶颈:资源的过度使用或分配不足 内部资源(如线程、放入存储池的对象等)匮乏,却无法判断是正常情况下随负载增加而引起的资源过度使用,还是由于资源泄漏引起 系统性能随负载的增加逐渐下降, 间发性的系统挂起或异常错误 若因资源分配不足引起,则可依照最大预期负载值上调存储池的最大容量;若因资源过度使用引起,请参看本表“外部系统的过度使用”一栏
不断重试 失败请求的频繁重试(某些极端情况下将无休止重试) 可预见的死锁系统突然出现混乱 后台系统可能已经完全宕机。监控系统可用性对这样的状况有所帮助,也可以只是将多次重复尝试的请求从成功的请求中筛选出来
线程阻塞点 线程退回到无法完成的同步点,造成通信阻塞 系统性能随负载的增加逐渐下降, 间发性的系统挂起或异常错误, 可预见的死锁, 系统突然出现混乱 或许根本没有必要进行同步(只需对系统重新设计稍加改良),当然也可以定制一些外在的锁定策略(如读取器或写入器的读/写锁)
线程的死锁或活锁 通常只是“获取顺序”问题 系统突然出现混乱 解决方法选项包括主锁、即定的获取顺序以及银行家算法
网络饱和 等待时间长或基本无法将任何请求打包,导致系统异常停运、系统挂起或活锁等情况的出现 系统持续缓慢运行, 系统性能随负载的增加逐渐下降, 间发性的系统挂起或异常错误 如此棘手的问题正在侵蚀着网络系统,如果不及时升级系统的基础架构,提升网络及路由器的运行速度,将无法扼制日后类似问题的出现

 

你可能感兴趣的:(J2EE性能问题的分析)