分类: Java
2008-11-24 13:58:23
本文将重点参考针对 JDK 1.3.1 和 JDK 1.4.2 的 IBM JVM Diagnostics Guides 。
这一节将介绍编写性能高效 Java 代码的一些通用准则,明确讨论如何避免对象创建和垃圾收集(GC),同时还将讨论 JNI、同步和数据结构。
只要有可能,应该避免创建对象,防止调用构造函数带来的相关性能成本,以及在对象结束其生命周期时进行垃圾收集所带来的成本。
考虑以下这些准则:System.gc()
调用避免在代码中调用 GC。GC 是一个“停止所有处理(stop the world)”的事件,它意味着除了 GC 线程自身外,其他所有执行线程都将处于挂起状态。如果必须调用 GC,那么可以在非紧急阶段或空闲阶段实现它。
使用本机代码编写应用程序部分,特别是频繁使用的部分,并将之与 Java 链接,这样做通常是为了提高性能。不过,JVM 与本机代码之间的通信通常很慢,因此,太多的 JNI 调用可能会降低性能。只要有可能就应该将本机操作集合在一起,以减少 JNI 调用的数量。
使用 JNI 代码本地处理异常,尽管有时不可避免地会导致性能下降。在这种情况下,应该使用 ExceptionCheck()
函数,因为与 ExceptionOccurred()
相比较,它带来的计算开销更少一些。后者必须创建一个将引用的对象,以及一个本地引用。
为了减少 JVM 和操作系统中的争用,应该只在可行的情况下才使用同步方法。不要将同步方法放到循环结构中。
作为一条通用规则,在更简单的数据结构能满足需要的地方,应该避免使用更复杂的数据结构。例如,在可以使用数组的地方不要使用向量。使用最有效的方法搜索元素,并将元素插入数据结构中,比如说,在向量的结尾处添加和删除元素,以便获得更好的性能。
用 -O
优化标记编译 Java 代码。代码优化提供了以下几个好处:
目前,通过调整 SPINLOOP 变量和时间片值,可以显示可获得的最大性能。IBM_LINUX_SPIINLOOP 时间值是一个进程在锁定之前可以在某个繁忙的锁上自旋的次数。有三个 SPINLOOP 变量可进行调整(从 0 到 100 的数字):
在 16 路 LPAR 上执行的基准测试认为以下设置将是最佳设置:
与其他任何全局变量一样,需要在 shell 实例中设置这些变量,JVM 进程将会在这个实例中运行,因此,可以通过 JVM 将这些设置读取到全局变量表中。
在可以运行内核 2.4.19 的 SLES8 上,有一个用于设置 Linux 内核中时间片的最大值和最小值的选项。这些都是通过 sysctl
命令设置的。为了获得好的 Java 性能,极力推荐将 sysctl 值 sched_yield_scale
设置为 1。
CLASSPATH 变量应该在搜索路径的前面包含一些最常使用的 Java 库。对于 LIBPATH and LD_LIBRARY_PATH 变量,也应该这样做,以便获得最常使用的 JNI 共享库。
为了获得最佳性能,让运行 JVM 进程的用户拥有经过正确配置的用户设置是很重要的。这些参数可以设置成以下两种形式之一:
ulimit
命令登录 shell 会话期间。
ulimit
语句添加到由登录 shell 读取的文件之一(例如 ~/.profile),即特定于 shell 的用户资源文件;或者通过编辑 /etc/security/limits.conf。 建议设置成无限制(unlimited)的一些重要设置是:
ulimit –d unlimited
ulimit –m unlimited
ulimit –s unlimited
ulimit –t unlimited
ulimit –v unlimited
对于需要做许多套接字连接并使它们处于打开状态的 Java 应用程序而言,最好通过使用 ulimit –n
,或者通过设置 /etc/security/limits.conf 中的 nofile
参数,为用户把文件描述符的数量设置得比默认值高一些。
“垃圾收集器”是影响 JVM 性能的最重要的 JVM 组件之一。关于 GC 和堆大小调优的一般性 IBM JVM 讨论(在针对 JDK 1.3.1 和 JDK 1.4.2 的 IBM JVM Diagnostics Guides 中)也可应用于 IBM JVM on Linux(包括 Linux on POWER),只是有一些 IBM JVM on Linux 特定的东西,后面会进行讨论。
最大堆大小是由 –Xmx
控制的,在 32 位 IBM JVM for Linux 上,可以将该值设置得比在 32 位 IBM JVM for AIX 上的更高一些,因为这两个操作系统的内存模式有所不同。如果没有指定 –Xmx
选项,则使用默认设置(即实际内存的一半,最小值是 16 MB,最大值是 512 MB)。
如果没用 –Xms
选项明确指定初始堆大小,那么该值是默认值 4 MB。有关 GC 和 Java 堆调优的更多信息,请参阅针对 JDK 1.3.1 和 JDK 1.4.2 的 IBM JVM Diagnostics Guides 中的“Debugging Performance Problems: JVM Performance”。“Understanding the Garbage Collector”和“Garbage Collector Diagnostics”这两章也值得一看。
就性能而言,JIT 是最重要的 JVM 组件。关于 IBM JVM JIT 的一般性讨论,请参阅 JVM Diagnostics Guide 中的“Understanding the JIT”小节。要获得关于 JIT 性能的 Linux 特定细节,请参阅“Linux Problem Determination”和“JIT Diagnostics”的 JIT 部分。
在 JVM Diagnostics Guide 的“Linux Problem Determination”一章中,详细地讨论了 IBM JVM for Linux 性能问题确定、JVM 监控和一些工具。
以下章节可能有其他的价值:
以下是一些线程模型实现方面的详细说明,该实现将影响不同 Linux 发行版本上的 JVM 性能。请参阅 JVM Diagnostics Guide 中的“Linux Problem Determination”一章,以了解更多细节。
另一个要知道的问题是 Linux 上的线程浮点堆栈限制,正如 JVM Diagnostics Guide 的“Floating Stacks Limitation”小节中所讨论的那样。
Nikolay Yevik 是 IBM eServer Solutions Enablement 团队的一名 Linux on POWER 技术顾问,有 6 年多的使用 C、C++ 和 Java 语言进行 UNIX 平台和性能开发的工作经验。他拥有石油工程和计算机科学硕士学位。您可以通过 yevik@us.ibm.com 与他联系。 |