最近的Java生产性能问题迫使我重新审视并真正欣赏Java VM即时(JIT)编译器。 大多数Java开发人员和支持人员都听说过这种JVM运行时性能优化,但是有多少人真正理解并欣赏它的好处?
本文将与您分享在添加新的虚拟服务器(容量改进和水平扩展项目)之后我所涉及的故障排除练习。
有关JIT的更深入介绍,我推荐以下文章:
- ##即时编译: http : //en.wikipedia.org/wiki/Just-in-time_compilation
- ## Java HotSpot性能引擎体系结构: http : //www.oracle.com/technetwork/java/whitepaper-135217.html
- ##了解即时编译和优化: http : //docs.oracle.com/cd/E15289_01/doc.40/e15058/underst_jit.htm
- ## JIT编译器如何优化代码: http : //pic.dhe.ibm.com/infocenter/java7sdk/v7r0/index.jsp?topic=%2Fcom.ibm.java.zos.70.doc%2Fdiag%2Funderstanding% 2Fjit_overview.html
JIT编译概述
JIT编译本质上是一个在运行时提高Java应用程序性能的过程。
下图说明了不同的JVM层和交互。 它描述了以下高级过程:
- Java编译器将Java源文件编译为平台无关的字节码或Java类文件。
- 在触发Java应用程序之后,JVM在运行时加载编译的类,并通过Java解释器执行适当的计算语义。
- 启用JIT后,JVM将分析Java应用程序方法调用并将字节码(在达到某些内部阈值之后)编译为本机的,更有效的机器代码。 通常,最繁忙的方法调用首先确定JIT进程的优先级。
- 将此类方法调用编译为机器代码后,JVM将直接执行它,而不是“解释”它。
- 随着时间的流逝,上述过程导致了运行时性能的提高。
案例分析
现在这里是我之前提到的项目的背景。 主要目标是在生产环境中添加新的IBM P7 AIX虚拟服务器(LPAR),以提高平台的容量。 查找以下平台本身的规格:
- Java EE服务器 : IBM WAS 6.1.0.37和IBM WCC 7.0.1
- 操作系统 :AIX 6.1
- JDK :IBM J2RE 1.5.0(SR12 FP3 + IZ94331)@ 64位
- RDBMS :Oracle 10g
- 平台类型 :中间层和批处理
为了达到现有的应用程序性能水平,购买了完全相同的硬件规格。 还使用与现有产品相同的版本安装了AIX OS版本和其他IBM软件。
为了确保应用程序具有相同的性能水平,对以下各项(清单)均进行了验证:
- 硬件规格(#CPU内核,物理RAM,SAN…)。
- 操作系统版本和补丁程序级别; 包括AIX内核参数。
- IBM WAS和IBM WCC版本,补丁程序级别; 包括调整参数。
- IBM JRE版本,补丁程序级别和调整参数(启动参数, Java堆大小 ……)。
- 还正确评估了网络连接性和性能。
新的生产服务器构建完成后,将执行功能测试,该测试还确认了联机和批处理应用程序的正确行为。
但是,在生产运营的第一天就发现了主要的性能问题。 您将在下面找到所观察到的性能问题的摘要矩阵。
生产服务器 | 运行时间 | 处理量(#个订单) | 中央处理器 % (平均) | 中间件健康 |
现有服务器 | 10个小时 | 25万(基线) | 20% | 健康 |
*新*服务器 | 10个小时 | 5万-500% | 80%+ 400% | 高线程利用率 |
从上面的视图中可以看到,性能结果在生产的第一天就非常糟糕。 与现有生产服务器相比,新生产服务器不仅处理了更少的订单,而且物理资源利用率(例如CPU%)也要高得多。
考虑到确保新服务器的构建与现有服务器完全相同所花费的时间,这种情况令人感到困惑。 那时,另一个核心团队参与进来,以执行额外的故障排除并确定性能问题的根源。
故障排除:寻找罪魁祸首…
故障排除团队分为两部分,以专注于以下项目:
- 从IBM WAS容器中识别CPU%的来源,并将CPU占用空间与现有生产服务器进行比较。
- 在现有生产服务器和新生产服务器之间执行更多数据和文件比较。
为了了解CPU%的来源,我们确实从运行IBM WAS和IBM WCC的IBM JVM中对每个线程执行了AIX CPU分析 。 从下面的屏幕快照中可以看到,发现许多线程各自使用5-20%。 在现有生产服务器上执行的相同分析确实显示出更少的线程数,CPU占用率始终在5%左右。
结论:
相同类型的业务流程使用的CPU是现有生产服务器的3-4倍。
为了了解执行的处理类型,每个线程数据在CPU的同时捕获了JVM线程转储 。 现在,在查看了JVM线程转储(Java内核)之后,我们意识到的第一件事就是JIT确实被禁用了! 通过从正在运行的JVM进程中运行java –version命令也可以确认该问题。
这个发现非常重要,特别是考虑到在现有生产服务器上启用了JIT。 大约在同一时间,负责比较服务器的另一个团队最终发现了用于启动应用程序的AIX用户的环境变量之间的差异。 早期的差距分析漏掉了这种比较练习。 他们发现新的AIX生产服务器具有以下额外条目:
JAVA_COMPILER=NONE
根据IBM文档 ,添加此类环境变量是禁用 JIT的方法之一。
复杂的根本原因分析,简单的解决方案
为了了解在我们的环境中禁用JIT的影响,您必须了解其含义。 禁用JIT本质上意味着整个JVM现在都在解释模式下运行。 对于我们的应用程序,以完全解释模式运行不仅会大大降低应用程序吞吐量,而且还会增加服务器CPU利用率的压力点,因为每个请求/线程占用的CPU数量是使用JIT执行的请求的3-4倍(请记住,当使用JIT时启用后,JVM将直接对机器/本机代码执行许多调用)。
如预期的那样,除去此环境变量以及重新启动受影响的JVM进程确实解决了问题并恢复了性能级别。
评估您的应用程序的JIT收益
我希望您对这个案例研究和对JVM JIT编译过程的简短回顾表示赞赏。 为了了解不对Java应用程序使用JIT的影响,建议您进行以下实验:
- 启用JIT并为您的应用程序生成负载,并捕获一些基准数据,例如CPU%,响应时间,#个请求等。
- 禁用JIT。
- 重做相同的测试并比较结果。
翻译自: https://www.javacodegeeks.com/2013/07/java-just-in-time-compilation-more-than-just-a-buzzword.html