本文的目的是以清单的方式提供BEA JRockit JVM的调优信息。从深奥的命令行选项到迭代性能测试,本文涵盖了许多方面。大部分数据都是我与用户合作过程中收集的。您要是也有什么技巧的话,请告诉我,在本文的下一版中,我会尝试将它们添加进去。
具体的产品版本信息都已在适当的地方列出;但是,本文所提供的通用指南适用于JRockit的大多数版本。每个版本的JRockit都增加了新的设置和优化,所以请查看 发行说明 和 JRockit产品中心。
首先需要确定您的运行时应用程序服务器所使用的JRockit的版本。为此,可以查看相应应用程序服务器的日志文件。也可以使用适当的脚本设置系统环境,然后执行java –version命令来确定JRockit的版本。
接着,收集当前JVM标志,开发和/或生产阶段需要用到它们:
-server -Xms1024m -Xmx1536m -Xverboselog:gc.log -Xverbose:memory -Xgcprio:throughput
这将告诉您当前JRockit实例的配置情况。
确定应用程序的目标是什么。是“响应快”还是“性能高”?根据目标的不同,需要设置不同的垃圾收集算法。
例如,如果应用程序的目标是实现高性能,则确保设置了Dynamic Garbage Collector "-Xgcprio:throughput" 选项。如果目标是响应时间短,那么需要将-Xgcprio:pausetime -Xpausetarget=XXX’中的pausetarget设置为最佳值。有关更多细节,请查看JRockit 调优文档。
如果JVM性能有问题,那么最好是先收集一些分析数据。该工作可以由团队中有相关经验的人员来完成,您也可以将这些信息发送给BEA Support做进一步分析。
首先,出现问题时需要收集大约10分钟的运行时JRockit Recording(JRA)数据。可以使用jrcmd.sh实用工具或JRockit Mission Control(JRMC)完成此操作。请阅读“性能测试期间的JRCMD/JRA”和“JRockit Mission Control”两节的内容。有关详细信息,请参阅 JRockit Mission Control文档。Latency Analysis一节提供许多有价值的内容,我们可以从中了解任何潜在的延迟问题(在JRockit中需要一个许可证就可以使用它)。
然后,需要收集问题发生时的一些详细日志。方法是在启动服务器实例的时候在JVM命令行输入以下参数:
-Xverboselog:perTestGC.log-Xverbose:opt,memory,gcpause,memdbg,compaction,gc,license-Xverbosetimestamp -Xgcreport
这样会将有价值的分析数据收集到刚才配置的perTestGC.log文件中。团队成员和/或BEA Support可以对这些数据进行分析。
最后一点:通常,应用程序不会请求执行垃圾收集(也就是在应用程序代码中调用System.gc())。但如果您怀疑它有问题,那么可以在启动服务器实例的时候,在Java命令行使用-XXnoSystemGC参数来禁用它。
现在,我将介绍如何通过迭代性能测试方法解决这些问题。
完成初始数据的收集和分析后,我们可以通过迭代方法来调优JVM。此处介绍的测试方案是在JRockit JVM层执行迭代调优的通用方法,可以找到哪些设置可能有益于特定应用程序。假定您有测量性能结果的方法;然后,可以将它们与“基准”(您应该已经有了) 进行比较。
在本测试中,我们将查看线程本地区域大小。这很重要,因为如果这些标志的默认设置对于应用程序不是最佳的(多数情况下是这样),那么就会造成堆锁定,这将对性能产生影响。将大部分对象限制在一定范围内对整体性能有益。
注意:为了确保在稳定状态下获取配置文件和度量,应用程序需要有足够的热启动时间。要在性能分析过程中检查是否符要求,可以查看JRA Recording中的优化标签,其中性能分析前后的优化数量和优化时间应该大致(理论上应该是准确地)相等。
现在让我们看一下锁性能分析,它会显示在应用程序中是否有过多的锁定。如果确实如此的话,那么将对整体性能造成影响。
-server -Xms1536m -Xmx1536m -Djrockit.lockprofiling
在这个测试中,我们将根据前面测试的结果调优线程本地区域大小和大对象限制。
本节测试的目的是运行各种不同的垃圾收集算法设置,并查看哪种设置对于应用程序最佳。关于 -XXsetGC标志 的详细令牌,请阅读以下内容。JRockit将以调优的nursery大小运行并移除-Xgcprio:throughput标志。该 throughput选项将在这些双版本的垃圾收集器之间自动切换,但进行直接的选择可能带来一些额外的性能上的好处。Nursery调优的目的是使被提 升的对象保持较少的数量,因为这是nursery收集中代价高的部分。通过增加和减小nursery的大小对其调优。nursery的大小主要依赖于对象 生存的时间,因为如果它们生存着,则在YC期间会得到提升。运行jrcmd <PID>版本查看哪个当前垃圾收集器策略是活动的。
-server -Xms1536m -Xmx1536m -Xns:384m -Xverboselog:perTestGC.log-Xverbose:opt,memory,gcpause,memdbg,compaction,gc,license-Xverbosetimestamp -Xgcreport -XXnoSystemGC -XXsetGC:singleparpar
-server -Xms1536m -Xmx1536m -Xns:384m -Xverboselog:perTestGC.log
-Xverbose:opt,memory,gcpause,memdbg,compaction,gc,license
-Xverbosetimestamp -Xgcreport -XXnoSystemGC -XXsetGC:genparpar
本测试的目的是查看gcthreads标志设置对整体性能的影响。
如果在胖锁(fat lock)上存在锁争用,则可以用-XXdisableFatSpin禁止它们,或者用-Djrockit.useAdaptiveFatSpin= true让JRockit自适应地禁止它们。当测试2启用-Djrockit.lockprofiling时,可以通过在JRA中查看那个标签来确定这一 点。更多细节,请参见locking in JRockit。
如果运行在Xeon硬件之上,那么可以添加-XXallocPrefetch和-XXallocRedoPrefetch,它们与TLA和LargeObjectLimit一起将有助于减少内存分配的开销。有关详细信息,请参见 allocPrefetch标志。为了得到最佳的结果,您可能会在BIOS中禁用硬件预取指令。虽然操作方式取决于BIOS的牌子,但参数的名称通常都为“Hardware Prefetcher”、“Adjacent Sector Prefetcher”、“Adjacent Cache Line Prefetcher”等。更多信息请参见 Intel on this subject。
这会将堆锁入内存,使操作系统不能将其交换出来。更多信息请参见 largePages标志,有关Linux操作系统端配置的更多信息还可以参考 在Linux上配置-XXlargePages 一节。在JRockit R27版中,该选项的名称为-XlargePages。根据前面的结果,调优-XlargePages标志可能有所帮助,但可能也没有。使用该标志运行测试,查看结果否对整体性能有帮助。
本节中的这些配置将使JVM高速运行并尽快达到稳定状态。为了实现此目标,JVM在启动时需要更多内部资源;但当目标一旦达成,它所需要的自适应优化将更少。我们推荐您为了那些单独工作的、运行时间长的、内存敏感的应用程序使用这个选项。更多细节请参见 aggressive标志。使用-XXaggressive标志运行测试,查看结果否对整体性能有帮助。
这个选项支持一个新的、更快的HashMap散列函数,它在Java 5.0 Update 8中引入,从R27.1.0开始也是BEA JRockit的一部分。这个散列函数能够通过改进的散列扩展提高性能而不改变HashMap中元素存放的顺序。更多细节请参见UseNewHashFunction标志。使用这个新的-XX:+UseNewHashFunction运行测试,,查看结果否对整体性能有帮助。
“暗物质”指被浪费的堆内存,它使堆成为许多碎片。了解如何最大程序地减少暗物质,以便当堆需要压缩时,整体吞吐量不受影响。请看以下选项:
最后,查看上述调优建议,调优WebLogic Server实例层。
本文提供的信息绝非一个完整的清单。但它会让您开始更好地理解和调优JRockit JVM层!
以下内容为正文引用过的额外信息。
使用命令行或者基于JRockit Mission Control (JRMC) Eclipse的工具都能够执行JRA Recoding。我们可以使用JRMC连接到多个 JRockit JVM,收集JRA recording,查看JVM的实时数据,检测和排除内存泄漏,以及查看应用程序内的潜伏物(执行缓慢“点”)。有关如何运行JRockit Mission Control,请参见下一节。
#ctrlhander.act file located in the <JROCKIT_HOME>/jre/bin/jrockit directoryset_filename filename=./jrocket_control_breakoutput.txt append=truetimestampprint_threadstimestampversionprint_class_summaryprint_object_summary increaseonly=trueprint_threadsprint_threads nativestack=trueprint_utf8pooltimestampprint_memusagetimestampheap_diagnosticstimestamp# The following is optional and is another way to generate a JRArecordingjrarecording filename=./myjra.xml time=600
问题:为何使用largePages?
回答:使用largePages的优点是可以锁定堆内存,并且不适用页面交换(还可以减少IOWait和GC)。在实际内存中,访问堆中的对象明显变快了。因此,为了实现性能目标,largePages选项是一个理想的选择。
HugePages_Total: xxxHugePages_Free: yyyHugepagesize: zzz KB
如果xxx为0,则没有分配任何大页面。
mkdir -p /mnt/hugepages mount -t hugetlbfs nodev /mnt/hugepages chmod 777 /mnt/hugepages
echo 20 > /proc/sys/vm/nr_hugepages
此处,数字20指应该保留的页面数量。要解除分配,可以将分配页面设置为0。(注意:请参见后面Q&A部分以确定正确的数量。)
如果并非所有请求的页面都被保留,那么可用内存就不够了。如果确要如此,内存很可能会有太多的碎片,那么我们的建议是重启机器。请注意,大页面不能被交换,所以一切都必须放在物理内存中。
在RHEL3上,该文件的名称为/proc/sys/vm/hugetlb_pool,所以可以使用以下命令执行:
echo 500 > /proc/sys/vm/hugetlb_pool
这里的数字500指请求的大小为多少MB,而不是页数。如果JRockit不能够直接消除临时的大页文件,请别忘记在执行之后进行消除。否则, 那些页在被释放之前将不可用。这对RedHat kernel build 2.4.18-e.25.smp适用,而2.4.18-e.12.smp却不行。
问题:如何确定发给/proc/sys/vm/nr_hugepages的正确数量呢?
回答:因为要将整个Java堆放入largePages,所以必须根据堆的大小和页面大小来确定。为/proc/sys/vm/nr_hugepages确定正确的数量就像下面的例子那样简单,信息如下:
JVM Max Heap = 1536MB (1572864 KB approx)HPAGE_SIZE = 2 MB then the value sent to /proc/sys/vm/nr_hugepages would be approximately 7.7. (1572864 MB / 2 MB)
注意:
尽管动态设置大页面的数量在理论上是可行的,但实践中并非如此。减少大页面的数量决不会有问题,但是增加大页面的数量就会产生问题。原因是,为了创建一个大页,Linux需要在内存中找到足够大的连续的区域。如果找不到,就不能创建大页面。
刚启动系统时,内存的碎片还不是很多,所以要找到足够大的连续的区域并不是问题。但机器运行时间一长,内存会产生更多的碎片。所以,如果要确保能够分配足够大的页面,就必须在启动刚完成时通过启动脚本或手动设置它们。