本文讨论的选项是针对HotSpot虚拟机的。 1、选项分类及语法HotspotJVM提供以下三大类选项: 1.1、标准选项这类选项的功能是很稳定的,在后续版本中也不太会发生变化。 运行java或者 java -help 可以看到所有的标准选项。 语法:所有的标准选项都是以 - 开头,比如-version,-server等。 1.2、X选项这类选项的功能还是很稳定,但官方的说法是它们的行为可能会在后续版本中改变,也有可能不在后续版本中提供了. 运行 java-X 命令可以看到所有的X选项。 语法:这类选项都是以 -X 开头,比如-Xms。 1.3、XX选项这类选项是属于实验性,主要是给JVM开发者用于开发和调试JVM的,在后续的版本中行为有可能会变化。 语法:
2、常用配置2.1、指定JVM运行模式Hotspot JVM有两种类型,分别是server和client。 它们的区别是Server VM的初始堆空间会大一些,默认使用的是并行垃圾回收器,启动慢运行快。Client VM相对来讲会保守一些,初始堆空间会小一些,使用串行的垃圾回收器,它的目标是为了让JVM的启动速度更快,但运行速度会比Serverm模式慢些。 JVM在启动的时候会根据硬件和操作系统自动选择使用Server还是Client类型的JVM。
更多详细内容可参见:http://docs.oracle.com/javase/7/docs/technotes/guides/vm/server-class.html
2.2、指定JIT编译器模式Java是一种解释型语言,但随着JIT技术的进步,它能在运行时将Java的字节码编译成本地代码。以下是几个相关的选项:
-Xcomp和-Xmixed到底谁的速度快,针对不同的程序可能有不同的结果,基本还是推荐用默认模式。
2.3、查看Java版本-version和-showversion -version用于查看当前在使用什么版本的java及JRE、什么类型的JVM(Server/Client)、采用什么编译器模式。示例如下: C:\Users\zsm>java -version java version "1.8.0_45" Java(TM) SE Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode) -showversion的作用是在运行一个程序时首先把上述信息打印出来,这样便于问题诊断。 建议Server类型的程序都把-showversion选项打开,这样可以发现一些配置问题,比如程序需要JDK1.7才能运行,而有的机器上装有多个JDK的版本,打开这个选项可以避免使用了错误版本的Java。
2.4、打印设置的XX选项及值有三个选项:-XX:+PrintCommandLineFlags、-XX:+PrintFlagsInitial、-XX:+PrintFlagsFinal -XX:+PrintCommandLineFlags:与-showversion类似,此选项可以在程序运行时首先打印出用户手动设置或者JVM自动设置的XX选项,建议加上这个选项以辅助问题诊断。 如下示例显示了JVM自动配置的初始和最大的HeapSize以及其他的一些选项: C:\Users\zsm>java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=132712832 -XX:MaxHeapSize=2123405312 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC java version "1.8.0_45" Java(TM) SE Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode) -XX:+PrintFlagsInitial:表示打印出所有XX选项的默认值。示例如下:
View Code
-XX:+PrintFlagsFinal:表示打印出XX选项在运行程序时生效的值。示例如下:
View Code
2.5、各区域内存大小设置注:虚拟机启动时就会把参数中设定大小的内存全部划为私有,即使扩容前有一部分内存不会被用户代码用到,这部分内存也不会交给其他进程使用。 2.5.1、方法区 永久代(Java8之前,Java8中没有永久代概念,下面参数无效) -XX:PermSize=256m:设置永久代初始值为256M。 -XX:MaxPermSize=256m:设置永久代最大值为256M。(同上) 元空间(Java8中) -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集器进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值 -XX:MaxMetaspaceSize,最大空间,默认没有限制。 2.5.2、栈 -Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。 2.5.3、堆 -Xmx3550m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为3550M。 -Xms3550m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。 -XX:NewSize=1024m:设置年轻代初始值为1024M。 -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。 -XX:SurvivorRatio=4:设置年轻代中Eden区与一个Survivor区的比值。表示Edgen为一个Survivor的4倍,即1个Survivor区占整个年轻代大小的1/6。 -XX:NewRatio=4:设置老年代与年轻代(包括1个Eden和2个Survivor区)的比值。表示老年代是年轻代的4倍。 -XX:PretenureSizeThreadshold=1024:设置让大于此阈值的对象直接分配在老年代(只对Serial、ParNew收集器有效),单位为字节 -XX:MaxTenuringThreshold=7: 表示一个对象如果在Survivor区移动了7次那下次MinorGC时就进入老年代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,可以在某种程度上提高服务稳定性。 -XX:+PrintTenuringDistribution:让JVM在每次MinorGC后打印出当前使用的Survivor中对象的年龄分布。示例: Desired survivor size 75497472 bytes, new threshold 15 (max 15) - age 1: 19321624 bytes, 19321624 total - age 2: 79376 bytes, 19401000 total - age 3: 2904256 bytes, 22305256 total 第一行显示Survivor容量及对象被移到老年代的年龄阈值。 第二行起,每行显示当前Survivor中某个年龄下对象的大小及小于等于该年龄的对象大小总和。因为目前Survivor空间中对象的大小22M小于期望Survivor空间的大小72M,所以没有对象会被移到老年代。 假设下一次MinorGC后的输出结果为: Desired survivor size 75497472 bytes, new threshold 2 (max 15) - age 1: 68407384 bytes, 68407384 total - age 2: 12494576 bytes, 80901960 total - age 3: 79376 bytes, 80981336 total - age 4: 2904256 bytes, 83885592 total 上次MinorGC后还存活的对象在这次MinorGC年龄都增加了1,可以看到上次年龄为2和3的对象(对应在这次GC后的年龄为3和4)依然存在(大小未变),而一部分上次对象年龄为1的对象在这次GC时被回收了。同时可以看到这次新增了约68M的新对象。这次MinorGC后Survivor区域中对象总的大小为约83M,大于了期望的Survivor空间的大小72M,因此它就把对象移到老年代的年龄的阈值调整为2,在下次MinorGC时一部分对象就会被移到老年代了。 相关的调整选项有: - -XX:InitialTenuringThreshold 表示对象被移到老年代的年龄阈值的初始值 - -XX:MaxTenuringThreshold 表示对象被移到老年代的年龄阈值的最大值 - -XX:TargetSurvivorRatio 表示MinorGC结束了Survivor区域中占用空间的期望比例。 这些参数的调节没有统一的标准,但是有两点可以借鉴: 2.5.4、直接内存 -XX:MaxDirectMemorySize:指定DirectMemory容量,若未指定,则默认与Java堆最大值一样。
每个进程能使用的内存大小受操作系统的限制,在堆等的内存大小不变时,配置的栈内存越小,能开启的线程数越多。
2.6、OutofMemory相关的选项如果程序发生了OOM后,JVM可以配置一些选项来做些善后工作,比如把内存给dump下来以分析原因,或者自动采取一些别的动作。
比如,下面的命令可以使得在发生OOM的时候,Heap被转存到文件/tmp/heapdump.hprof,同时执行Home目录中的cleanup.sh文件: java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh" MyApp
2.7、收集器设置2.7.1、吞吐量优先收集器的相关选项 衡量JVM垃圾收集器的两个基本指标是吞吐量和停顿时间。 吞吐量是指执行用户代码的时间占总的时间的比例,总的时间包括执行用户代码的时间和垃圾回收占用的时间。在垃圾回收的时候执行用户代码的线程必须暂停,这会导致程序暂时失去响应。停顿时间就是衡量垃圾回收时造成的用户线程暂停的时间。这两个指标在一定程度是相互矛盾的,不可能让一个程序的吞吐量很高的同时停顿时间也短,只能以优先选择一个目标或者折中一下。因此,不同的垃圾回收器会有不同的侧重点。 在Hotspot JVM中,侧重于吞吐量的垃圾回收器是Parallel Scavenge和Parallel Old,它的相关选项如下:
此外,如果机器只有一个核的话,采用并行回收器可能得不偿失,因为多个回收线程会争抢CPU资源,反而造成更大的消耗。这时,就最好采用串行回收器,相关的参数是-XX:+UseSerialGC 2.7.2、CMS收集器 CMS收集器(ConcurrentMarkandSweep),是一个关注系统停顿时间的收集器。它的主要思想是把收集器分成了不同的阶段,其中某些阶段是可以用户程序并行的,从而减少了整体的系统停顿时间。它主要分成了以下几个阶段: - 初始标记 initial mark CMS虽然能减少系统的停顿时间,但是它也有其缺点: 当上面的任何一种情况发生的时候,JVM就会触发一次Full GC,会导致JVM停顿较长时间。 它的相关选项如下:
2.8、GC日志相关的选项分析GC问题不可避免地要查看GC日志,下面是一些GC日志相关的选项:
[GC 425355K->351685K(506816K), 0.2175300 secs] [Full GC 500561K->456058K(506816K), 0.6421920 secs] 其中以GC开头的行表示发生了一次Minor GC,后面的数字表示收集前后Heap空间的占用量,圆括号里面表示Heap大小,最后的数字表示用了多少时间。比如:上面的例子中,表示在这次GC新生代空间占用从425355K降到了351685K,总的新生代空间为506816K,这次GC耗时0.22秒。
比如,以下是-XX:+PrintGCTimeStamps的输出 0,185: [GC 66048K->53077K(251392K), 0,0977580 secs] 0,323: [GC 119125K->114661K(317440K), 0,1448850 secs] 0,603: [GC 246757K->243133K(375296K), 0,2860800 secs] 以下是两个都打开后的输出: 2014-12-26T17:52:38.613-0800: 3.395: [GC 139776K->58339K(506816K), 0.1442900 secs]
需要注意的是:这些和GC日志相关的选项可以在JVM已经启动后再开启,可以通过jinfo这个工具去设置。具体可以参见jinfo的帮助文件。这样就可以在需要诊断问题的时候再开启GC日志。
最后给一个示例: -Xmx3550m:设置JVM最大堆内存为3550M。 -Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 -Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。 -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。 -XX:NewSize=1024m:设置年轻代初始值为1024M。 -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。 -XX:PermSize=256m:设置持久代初始值为256M。 -XX:MaxPermSize=256m:设置持久代最大值为256M。 -XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。 -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。 -XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
3、Java代码获取JVM相关参数可以借助java.lang.management.*:
代码示例:
View Code
输出:
View Code
4、参考资料(转载请注明出处,谢谢) |
|