Java性能调优

pooi 10年前

调优的方法

这一节包含了你调优Java应用可以采用的不同选择。基于这些选择的比较应该采用我们刚才讨论的统计学方法来进行。

一般性的调优准则

这里是一些基本的调优准则,帮助你把不同的调优方法进行分类。

了解Java的动态调优机制

在你开始调优Java启动的命令行参数之前,请注意,Sun HotSpot  Java虚拟机具备了调整自身的特性。这种智能的自我调整称为“Ergonomics”。大多数具备2颗CPU和2G以上物理内存的机器都可以被看成是服务器级别(server-class)的机器。这意味着一下选项被缺省打开:

  • -server 编译器
  • -XX:+UseParallelGC 并行的垃圾回收
  • -Xms 初始堆大小为机器物理内存的1/64
  • -Xmx 堆大小的上限是机器物理内存的1/4 (不超过1G)

请注意,32位的Windows系统都缺省使用-client编译器,64位系统如果满足上面标准的话被认为是server-class机器。

堆的大小调整

尽管“Ergonomics”机制大大提升了许多应用“开箱即得”的性能,我们仍然需要对Java内存大小调整有足够的重视。

一个Java应用程序可以使用的最大的堆的大小取决于下面三个因素:

  • 进程数据模型(32-bit还是64-bit),以及相应的操作系统限制
  • 系统可用的虚拟内存
  • 系统可用的物理内存

一个特定Java应用程序的堆的大小不可能超过进程数据模型的最大虚拟内存限制。对于一个32位的进程模型,虚拟内存的地址大小是4G,然而有些操作系统会限制到2G或3G。典型的堆的最大设定值是:-Xmx3800m (1600m - 2G的情况),具体的限制和应用本身也有关。对于64位进程模型,最大值基本上可以认为没有限制。对于一个特定机器上的Java应用,Java堆的大小永远不能设成物理内存的大小,因为额外的内存需要保留给操作系统,其他进程,甚至其他的Java虚拟机使用。使用太多的系统内存很容易引起虚拟内存和磁盘之间的交换,特别是在垃圾回收的时候,导致严重的性能问题。在那些有多个Java应用的环境,或者多个应用的环境里,这些进程的堆的总和不应该超过系统物理内存的大小。

另一个非常重要的可调参数是Young Generation(也就是NewSize)的大小。通常来讲,Young Generation的最大值是堆大小的3/8。

垃圾回收策略

JavaTM平台提供了垃圾回收算法的选择。对于每一种算法存在有许多个可调参数。通常来说,下面的前两个是大型服务器应用最常用的选择:

  • -XX:+UseParallelGC 并行(吞吐)垃圾回收
  • -XX:+UseConcMarkSweepGC 并发(低暂停时间)垃圾回收
  • -XX:+UseSerialGC 串行垃圾回收(对小的应用和系统)
其它调优参数

通过适当地设置操作系统内存页面以及使用命令行参数-XX:+UseLargePages以及-XX:LargePageSizeInBytes,你可以从你的系统的内存管理系统中得到最好的效率。请注意,大的PageSize使我们能够更好地利用虚拟内存资源(TLB),但是这也使得 Permanent Generation和Code Cache的尺寸变大,从而迫使你减小Java内存堆的大小。对于2MB或者4MB的内存页面问题或许不大,但是对于256MB的内存页,就值得仔细推敲了。

一个针对Solaris环境的例子就是选择libumem作为内存堆的分配器。为了体验libumem,你可以通过设置LD_PRELOAD环境变量来完成:

  • 使一个shell中新的进程使用libumem
LD_PRELOAD=/usr/lib/libumem.so
  • 在sh中使用libumem来启动Java程序
LD_PRELOAD=/usr/lib/libumem.so java java-settings application-args
  • 在csh中使用libumem来启动Java程序
env LD_PRELOAD=/usr/lib/libumem.so java java-settings application-args
通过pldd或者pmap命令,你可以确定libumem是否被使用。
调优实例高吞吐量

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

并行的old generation收集

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

使用256MB内存页

java -Xmx2506m -Xms2506m -Xmn1536m -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:LargePageSizeInBytes=256m

比较激进的优化

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:+AggressiveOpts

使用有偏向性的锁策略

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:+AggressiveOpts -XX:+UseBiasedLocking

低延迟和高吞吐

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31

监控和性能测量

讨论监控(抽取一个程序运行的粗略统计数据)和性能测量(借助工具获得程序运行性能的细节)是需要单独的白皮书来论述的。这里借助一些例子来说明Java性能调优时可以用到的工具。

监控

Java平台本身包含大量的监控工具,最流行的是JConsole和jvmstat

性能测量

Java平台本身包含一些性能测量工具,最流行的是-Xprof Profiler和HPROF Profiler。一个基于JFluid技术的profiler插件被集成 了NetBeans环境里面。

写高性能的代码

NIO(New I/O)API针对内存映射文件和可扩展的网络操作,提供了更好的性能。通过使用NIO,那些频繁使用内存或网络的应用程序将得到巨大的性能提升。一个很好的例子就是Glassfish当中的Grizzly Web Container。

另一个影响程序性能的Java新特性是Concurrency Utilities。越来越多的应用跑在了多CPU多核的服务器上。为了充分利用这一特性,程序必须设计成多线程的。传统的多线程编程架构过于复杂,线程之间的交互容易引起错误。有了Concurrency Utilities之后,开发人员就拥有了一整套设计模块。使用它们开发多线程应用程序将变得事半功倍