Mission ControlBEA JRockit JVM 自带的一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。它包括三个独立的应用程序:内存泄漏监测器( Memory Leak Detector )、 JVM 运行时分析器( Runtime Analyzer )和管理控制台( Management Console )。 BEAJRockit R26 版本就开始捆绑这个工具套件,目前最新的版本是 3.0 。最近我们使用其中的 Runtime Analyzer 对国内某著名行业解决方案进行性分析和调优。
 
JRockit Runtime AnalyzerJRA )是一个 JVM 分析器,是一个随需应变的“动态记录器”。它记录了 Java 应用程序和 JVM 在一段预定的时间内的详细记录。然后通过 JRA 应用程序对记录下来的文件进行离线分析。所记录的数据包括对方法的调用跟踪、错误的同步、锁定的分析,还有垃圾收集统计信息,优化决策以及对象统计信息和其他重要的应用程序 /JVM 行为。它的目的是让 JRockit 开发人员能够找到良好的方法来基于现实应用程序优化 JVM ,对于帮助客户在生产和开发环境中解决问题十分有用。
 
2. 性能数据分析和调优
 
在本次项目中,操作 |A 和操作 B 的百人并发脚本执行完成的时间接近两分钟,因此我们使用 JRA 进行了 2 分钟 (120) 的记录。在 GC 常规信息中,我们发现在短短两分钟时间内,垃圾收集的总数高达 365 次,而由此造成的暂停时间有 42.5 秒之多。也就是说 35% 的执行时间是在做垃圾收集。
 
因为最大堆尺寸已经设置成 1024M ,对于 32 位操作系统上的 Java 应用已经是足够大了(在 IA32 构架下,由于操作系统给每个进程的最大内存寻址空间为 1.8G ,因此最大堆尺寸不能超过 1.8G ),因此堆的大小并不是造成频繁垃圾收集的原因。那么在高并发度的场景下,可能的影响因素很可能是 Nursery 大小。
 
Nursery 也称为新代,是指运行分代式垃圾收集器时,在堆中分配 新对象 的可用块区域。 当 Nursery 变满时,会在新垃圾收集中单独对其进行垃圾收集。 Nursery 大小决定了新收集的频率和持续时间。较大 Nursery 会降低收集的频率,但是会稍微增加每个新收集的持续时间。 Nursery 之所以具有价值,是因为 Java 应用程序中的大多数对象都是在新代中夭亡的。与收集整个堆相比,应首选从新空间中收集垃圾,因为该收集过程的开销更低,而且在触发收集时,新空间中的大多数对象均已死亡。 在新收集过程中, JVM 首先确定 Nursery 中的哪些对象是活动的,此后将它们提升到旧空间,并释放 Nursery ,供分配新的小对象使用 。
 
Nursery 的默认缺省值是 10M/CPU ,对于我们 Clovertown 服务器来说,只有 20M 。由于出现频繁收集的情况,那么我们推断是由于 Nursery 的默认值太低的原因。一方面在高并发用户的场景下,肯定是有大量的新对象产生,那么 Nursery 的空闲空间很容易就被耗尽。因此 Nursery 发生垃圾收集频率就会比较高。另一方面更短的垃圾收集间隔会使得新对象在 Nursery 的存活率提高因为很多新对象可能还没来得及使用完毕就已经发生垃圾收集。这样更多的对象会被提升到旧代,使得旧代的对象也会急剧增加,从而使得旧代发生垃圾收集的频率也增加。
 
因为 JRockit JVM 可以使用 -Xns:<size> 来设置 Nursery 的尺寸,我们要在保证垃圾回收停顿时间( garbage collection-pause )尽可能短的同时,尽量加大 Nursery 的尺寸,这在创建了大量的临时对象时尤其重要。推荐值是最大堆尺寸的 10% ,因此我们在 JRockit 的运行时参数上添加了 –Xns100m 。再次运行脚本后, JRA 收集的信息显示 GC 暂停时间骤降到 15.3s ,次数也有所减少,降到 296
 
Nursery 大小
20M( 默认值 )
100M
GC 暂停时间
42.5s
15.3s
垃圾收集的总数
365
296
平均暂停时间
116ms
52ms
 
此外,我们从方法信息中可以看到调用次数最多耗时间最长的两个方法分别是 jrockit.vm.Locks.monitorEnterSecondStagecom.ABC.StateManager.makeState 两个方法。展开前置任务后发现调用这两个方法最多的方法是 com.ABC.SqlQueryAction.query 。而 jrockit.vm.Locks.monitorEnterSecondStage 显然是 JRockit 实现锁机制的特定的 API 。因此我们怀疑是对数据库的操作时有资源互斥的现象发现。
 
考虑到高并发用户的场景下,对数据库操作的并发度也很高,因此对数据库连接的争用比较激烈。我们察看了一下当时 WebLogic JDBC 的配置,发现 connection pool 的大小只是缺省值 20 ,相对来说偏小了,对性能会有一定的影响。因此我们增大 connection pool 的大小到 100 。重新运行测试脚本后发现性能有较大提升。
 
 
 
JDBC connection size 20  w/ default nursery
JDBC connection size 100 w/ 100M nursery
Increase %
 
操作 A
22.125
12.079
83%
操作 B
35.195
21.773
62%
 
 
在性能调优完成后,我们又进行了功能测试(回归测试),以验证上述改动没有影响系统的功能性正确。
四、小结
其实利用 Mission ControlJava 应用进行调优并不难,对吧?希望本次性能分析调优的过程可以给大家一些启发,今后可以应用到日常工作中。