分享

HotSpot JVM常用参数(选项)设置

 关平藏书 2017-08-01

本文讨论的选项是针对HotSpot虚拟机的。

1、选项分类及语法

HotspotJVM提供以下三大类选项:

1.1、标准选项

这类选项的功能是很稳定的,在后续版本中也不太会发生变化。

运行java或者 java -help 可以看到所有的标准选项。

语法:所有的标准选项都是以 - 开头,比如-version,-server等。

1.2、X选项

这类选项的功能还是很稳定,但官方的说法是它们的行为可能会在后续版本中改变,也有可能不在后续版本中提供了.

运行 java-X 命令可以看到所有的X选项。

语法:这类选项都是以 -X 开头,比如-Xms。

1.3、XX选项

这类选项是属于实验性,主要是给JVM开发者用于开发和调试JVM的,在后续的版本中行为有可能会变化。 

语法:

  • 如果是布尔类型的选项,它的格式为-XX:+flag或者-XX:-flag,分别表示开启和关闭该选项。
  • 针对非布尔类型的选项,它的格式为-XX:flag=value

2、常用配置

2.1、指定JVM运行模式

Hotspot JVM有两种类型,分别是server和client。

它们的区别是Server VM的初始堆空间会大一些,默认使用的是并行垃圾回收器,启动慢运行快。Client VM相对来讲会保守一些,初始堆空间会小一些,使用串行的垃圾回收器,它的目标是为了让JVM的启动速度更快,但运行速度会比Serverm模式慢些。

JVM在启动的时候会根据硬件和操作系统自动选择使用Server还是Client类型的JVM。 

  • 在32位Windows系统上,不论硬件配置如何,都默认使用Client类型的JVM。 
  • 在其他32位操作系统上,如果机器配置有2GB以上的内存同时有2个以上的CPU,则默认会使用Server类型的JVM 
  • 64位机器上只有Server类型的JVM。也就是说Client类型的JVM只在32位机器上提供。 
  • 也可以使用-server和-client选项来指定JVM的类型,不过只在32位的机器上有效,原因见上面一条。 

更多详细内容可参见:http://docs.oracle.com/javase/7/docs/technotes/guides/vm/server-class.html

 

2.2、指定JIT编译器模式

Java是一种解释型语言,但随着JIT技术的进步,它能在运行时将Java的字节码编译成本地代码。以下是几个相关的选项:

  • -Xint表示禁用JIT,所有字节码都被解释执行,这个模式的速度最慢的。
  • -Xcomp表示所有字节码都首先被编译成本地代码,然后再执行。
  • -Xmixed,默认模式,让JIT根据程序运行的情况,有选择地将某些代码编译成本地代码。

-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区域中占用空间的期望比例。

这些参数的调节没有统一的标准,但是有两点可以借鉴: 
1、如果Survivor中对象的年龄分布显示很多对象在经历了多次GC最终年龄达到了-XX:MaxTenuringThreshold才被移到老年代,这可能说明-XX:MaxTenuringThreshold设置得过大,也有可能是Survivor的空间过大。 
2、如果-XX:MaxTenuringThreshold的值大于1,但是很多对象年龄都不大于1,那就得关注一下期望的Survivor空间。如果每次GC后Survivor中对象的大小都没有超过期望的Survivor空间大小,则说明GC工作得很好。反之,则说明可能Survivor空间小了,使得新生成的对象很快就被移到了老年代了。

 

2.5.4、直接内存

-XX:MaxDirectMemorySize:指定DirectMemory容量,若未指定,则默认与Java堆最大值一样。

 

每个进程能使用的内存大小受操作系统的限制,在堆等的内存大小不变时,配置的栈内存越小,能开启的线程数越多。

 

2.6、OutofMemory相关的选项

如果程序发生了OOM后,JVM可以配置一些选项来做些善后工作,比如把内存给dump下来以分析原因,或者自动采取一些别的动作。

  • -XX:+HeapDumpOnOutOfMemoryError 表示在内存出现OOM的时候,把Heap转存(Dump)到文件以便后续分析,文件名通常是java_pid<pid>.hprof,其中pid为该程序的进程号。
  • -XX:HeapDumpPath=<path> 用来指定heap转存文件的存储路径,需要指定的路径下有足够的空间来保存转存文件。
  • -XX:OnOutOfMemoryError 用来指定一个可行性程序或者脚本的路径,当发生OOM的时候,去执行这个脚本。

比如,下面的命令可以使得在发生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,它的相关选项如下:

  • -XX:+UseParallelOldGC 表示新生代和老生代都使用并行回收器,其中的Old表示老生代的意思,而不是旧的意思。
  • -XX:ParallelGCThreads=n 表示配置多少个线程来回收垃圾。默认的配置是如果处理器的个数小于8,那么就是处理器的个数;如果处理器大于8,它的值就是3+5N/8。也可以根据程序的需要去设置这个值,比如你的机器有16核,上面有4个Java程序,那么设置将这个值设置为4比较合理,因为JVM不会去探测同一机器上有多少个Java程序。
  • -XX:UseAdaptiveSizePolicy 表示是否开启自适应策略,打开这个开关后,JVM自动调节JVM的新生代大小,Eden和Survivor的比例等参数。用户只需要设置期望的吞吐量(-XX:GCTimeRatio)和期望的停顿时间(-XX:MaxGCPauseMillis)。然后,JVM会尽量去向用户期望的方向去优化。

此外,如果机器只有一个核的话,采用并行回收器可能得不偿失,因为多个回收线程会争抢CPU资源,反而造成更大的消耗。这时,就最好采用串行回收器,相关的参数是-XX:+UseSerialGC

2.7.2、CMS收集器

CMS收集器(ConcurrentMarkandSweep),是一个关注系统停顿时间的收集器。它的主要思想是把收集器分成了不同的阶段,其中某些阶段是可以用户程序并行的,从而减少了整体的系统停顿时间。它主要分成了以下几个阶段: 

- 初始标记 initial mark 
- 并发标记 concurrent mark 
- 重新标记 remark 
- 并发清理 concurrent clean 
- 并发重置 concurrent reset

CMS虽然能减少系统的停顿时间,但是它也有其缺点: 
1. 从它的名字可以看出,它是一个标记-清除收集器,也就说运行了一段时间后,内存会产生碎片,从而导致无法找到连续空间来分配大对象。 
2. CMS收集器在运行过程中会占用一些内存,同时系统还在运行,如果系统产生新对象的速度比CMS清理的速度快的话,会导致CMS运行失败。

当上面的任何一种情况发生的时候,JVM就会触发一次Full GC,会导致JVM停顿较长时间。

它的相关选项如下: 

  • -XX:+UseConcMarkSweepGC 表示老年代开启CMS收集器,而新生代默认会使用并行收集器。 
  • -XX:ConcGCThreads 指定用多少个线程来执行CMS的并非阶段。 
  • -XX:CMSInitiatingOccupancyFraction 指定在老生代用掉多少内存后开始进行垃圾回收。与吞吐量优先的回收器不同的是,吞吐量优先的回收器在老生代内存用尽了以后才开始进行收集,这对CMS来讲是不行的,因为吞吐量优先的垃圾回收器运行的时候会停止所有用户线程,所以不会产生新的对象,而CMS运行的时候,用户线程还有可能产生新的对象,所以不能等到内存用光后才开始运行。比如-XX:CMSInitiatingOccupancyFraction=75表示老生代用掉75%后开始回收垃圾。默认值是68。 
  • -XX:+ExplicitGCInvokesConcurrent 如果在代码里面显式调用System.gc(),那么它还是会执行Full GC从而导致用户线程被暂停。采用这个选项使得显式触发GC的时候还是使用CMS收集器。 
  • -XX:+DisableExplicitGC 一个相关的选项,这个选项是禁止显式调用GC

2.8、GC日志相关的选项

分析GC问题不可避免地要查看GC日志,下面是一些GC日志相关的选项:

  • -XX:+PrintGC,等同于-verbose: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:+PrintGCDetails 在发生垃圾回收时打印内存回收日志,并在进程退出时输出当前内存各区域分配情况。

  • -XX:+PrintGCTimeStamps  打印GC发生时相对于JVM启动时的时间。

  • -XX:+PrintGCDateStamps  打印出GC发生的具体时间。

比如,以下是-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]
  • -Xloggc:<file> 表示把GC日志写入到一个文件中去,而不是打印到标准输出中。
  • -XX:+PrintHeapAtGC  每一次GC前和GC后,都打印堆信息。
  • -XX:+TraceClassLoading  监控类的加载。会打印 [Opened C:\Program Files\java\jdk1.8.0_45\jre\lib\rt.jar] [Loaded java.lang.Object from C:\Program Files\java\jdk1.8.0_45\jre\lib\rt.jar]... 。
  • -XX:+PrintGCApplicationStoppedTime 打印GC时线程的停顿时间,如: Total time for which application threads were stopped: 0.0000660 seconds, Stopping threads took: 0.0000134 seconds 。

需要注意的是:这些和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.*

java.lang.management: for monitoring and management of the Java virtual machine and other components in the Java runtime. It allows both local and remote monitoring and management of the running Java virtual machine. 

代码示例:

View Code

输出:

View Code

 

4、参考资料

[1]Hotspot JVM的常用选项

 




(转载请注明出处,谢谢)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多