Jvm启动参数配置建议

JVM启动参数配置建议 #

// 内存大小参数
-Xms6g
-Xmx6g

-XX:MetaspaceSize=64m
-XX:MaxMetaspaceSize=256m

-Xss256k

-XX:MaxDirectMemorySize=2G

// 性能参数
-XX:+AlwaysPreTouch
-XX:+OmitStackTraceInFastThrow
-XX:+ExplicitGCInvokesConcurrent
-XX:-UseBiasedLocking
-XX:AutoBoxCacheMax=20000
-XX:+UseStringDeduplication

// G1 GC相关参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:InitiatingHeapOccupancyPercent=45
-XX:MaxTenuringThreshold=6
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=4
-XX:G1HeapRegionSize=16m
-XX:G1ReservePercent=10
-XX:+ParallelRefProcEnabled

// GC日志相关参数
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=50m
-Xloggc:/dev/shm/gc.log
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+PrintGCTimeStamps 
-XX:+PrintGCApplicationConcurrentTime 
-XX:+PrintGCApplicationStoppedTime 

// 内存溢出自动dump堆内存
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/root/logs/{appName}/java_heapdump.hprof	// {appName}替换应用名称

详细解释 #

内存大小参数 #

-Xms6g
-Xmx6g

单位:k/K、m/M、g/G

设置堆内存的初始大小和最大大小,一般建议设置成相同,避免扩容带来的损耗


-XX:MetaspaceSize=64m
-XX:MaxMetaspaceSize=256m

设置元空间的初始大小和最大大小,最大值默认没有限制


-Xss256k

等价于-XX:ThreadStackSize,设置每个线程的栈大小为256k


-XX:MaxDirectMemorySize=2G

指定DirectMemory容量,若未指定,则默认为Heap区总内存减去一个Survivor区的大小


性能参数 #

-XX:+AlwaysPreTouch

在没有配置-XX:+AlwaysPreTouch参数即默认情况下,JVM参数-Xms申明的堆只是在虚拟内存中分配,而不是在物理内存中分配:它被以一种内部数据结构的形式记录,从而避免被其他进程使用这些内存。这些内存页直到被访问时,才会在物理内存中分配。当JVM需要内存的时候,操作系统将根据需要分配内存页

配置-XX:+AlwaysPreTouch参数后,JVM将-Xms指定的堆内存中每个字节都写入'0’,这样的话,除了在虚拟内存中以内部数据结构保留之外,还会在物理内存中分配。并且由于touch这个行为是单线程的,因此它将会让JVM进程启动变慢。所以,要么选择减少接下来对每个缓存页的第一次访问时间,要么选择减少JVM进程启动时间,这是一种trade-off

总结:

  1. 开启这个参数,可以在服务启动的时候真实地分配物理内存给JVM
  2. 如果没开启,分配的只是虚拟内存,当真正使用的时候才会分配物理内存,实时分配会影响性能
  3. 开启这个参数,程序启动时间会减慢很多

-XX:+OmitStackTraceInFastThrow

在开启此开关后(默认开启),程序运行中如果大量报如下几种异常:

  • NullPointerException
  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • ArrayStoreException
  • ClassCastException

在达到一定数量之后,为了打日志的操作不影响性能,后续的异常,异常栈信息会被清空,即打到日志文件中的异常信息会变成类似如下的样子(没有调用栈信息)

... ...
java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException
... ...

-XX:+ExplicitGCInvokesConcurrent

如果是 System.gc() 触发的 GC,G1 GC 会根据 ExplicitGCInvokesConcurrent 这个 JVM 参数决定是默认GC(轻量 GC,YoungGC)还是FullGC。这在一些情况下,比如使用了 Direct 内存,为了使得其(堆外内存)能够被及时回收,我们会通过显式调用 System.gc() 触发 full gc。但是 full gc 又会导致 stw,这又是我们不想看到的。开启这个开关后遇到显式触发的GC会使用轻量GC


-XX:-UseBiasedLocking

JDK1.6开始默认打开的偏向锁,会尝试把锁赋给第一个访问它的线程,取消同步块上的synchronized原语。如果始终只有一条线程在访问它,就成功略过同步操作以获得性能提升

但一旦有第二条线程访问这把锁,JVM就要撤销偏向锁恢复到未锁定线程的状态,虽然只是很短很短的停顿,但对于多线程并发的应用,取消掉它 -XX:-UseBiasedLocking 反而有性能的提升和延时的极微的缩短


-XX:AutoBoxCacheMax=20000

提高数字缓存,Integer i = 3;这语句有着 int自动装箱成Integer的过程,JDK默认只缓存 -128 ~ +127的int 和 long,超出范围的数字就要即时构建新的Integer对象

使用-XX:AutoBoxCacheMax=n 参数即可将Integer的自动缓存区间设置为[-128, n]。注意区间的下界固定在-128不可配置

在未设置此参数的情况下,下边的代码执行结果如下:

public static void main(String[] args) {
	Integer a = -128;
	Integer aa = -128;
	Integer b = -129;
	Integer bb = -129;
	Integer c = 20000;
	Integer cc = 20000;
	Integer d = 20001;
	Integer dd = 20001;
	System.out.println(a == aa);	// true
	System.out.println(b == bb);	// false
	System.out.println(c == cc);	// false
	System.out.println(d == dd);	// false
}

设置了之后,下边的代码执行结果如下:

public static void main(String[] args) {
	Integer a = -128;
	Integer aa = -128;
	Integer b = -129;
	Integer bb = -129;
	Integer c = 20000;
	Integer cc = 20000;
	Integer d = 20001;
	Integer dd = 20001;
	System.out.println(a == aa);	// true
	System.out.println(b == bb);	// false
	System.out.println(c == cc);	// true
	System.out.println(d == dd);	// false
}

// 说明在[-128, 20000]以内的Integer对象,都是使用的缓存,没有创建新的对象

同样对于垃圾回收来说:

Integer i = 100;
i = null;	// 由于100使用的是Integer(100)的缓存,所以这个操作不会产生垃圾

所以增加数字缓存,还可以减少垃圾的产生,间接减少垃圾回收次数,提升程序的吞吐量


-XX:+UseStringDeduplication

开启字符串去重,默认禁用。主要是用来消除长时间存活的重复字符串对象,它不会对短期存活的对象做去重。如果字符串的生命周期很短,很可能还没来记得做去重就已经死亡了

默认情况下,一个字符串对象经过3次GC以后还存活才会被列为去重的候选对象,可以用-XX:StringDeduplicationAgeThreshold来改变经历的GC的次数,比如 -XX:StringDeduplicationAgeThreshold=6

因为是在GC的时候做的字符串去重,因此可能会增加GC的停顿时间。但是,如果有很高的去重率可能会抵消掉这部分影响,因为去重以后可以减轻GC的其他阶段的时间花费,同时还可以减少GC的频率(因为减少了堆的大小)

-XX:+UseStringDeduplication并不会消除重复字符串的引用本身,它只会替换底层的char[],消除重复字符串对象只需要对value字段进行重新引用赋值即可, 差不多就是这个意思:aString.value = anotherString.value

任何一个String对象在内存中最少占用24个字节,启用这个参数以后如果有很多短的重复字符串对象的话会大大的节省内存

如果你想查看字符串去重的一些统计信息,比如说去重花了多长时间、多少重复字符串被去重、节省了多少内存等等,可以传递-XX:+PrintStringDeduplicationStatistics这个参数给JVM,在GC日志中就可以打印出这些信息来


GC相关参数 #

-XX:+UseG1GC

手动指定使用G1收集器执行内存回收任务


-XX:G1HeapRegionSize=16m

设置每个Region的大小。值是2的幂,范围是1MB到32MB之间,目标是根据最小的Java堆大小划分出约2048个区域。默认是堆内存的1/2000

建议:程序中大对象多的话,建议设置16M或者32M


-XX:MaxGCPauseMillis=200

设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到)。默认值是200ms

注意:每当对垃圾回收进行评估或调优时,都会涉及到延迟与吞吐量的权衡。G1 GC 是增量垃圾回收器,暂停统一,同时应用程序线程的开销也更多。G1 GC 的吞吐量目标是 90% 的应用程序时间和 10%的垃圾回收时间。如果将其与 Java HotSpot VM 的吞吐量回收器相比较,目标则是 99% 的应用程序时间和 1% 的垃圾回收时间。因此,当您评估 G1 GC 的吞吐量时,暂停时间目标不要太严苛。目标太过严苛表示您愿意承受更多的垃圾回收开销,而这会直接影响到吞吐量。当您评估 G1 GC 的延迟时,请设置所需的(软)实时目标,G1 GC 会尽量满足。副作用是,吞吐量可能会受到影响


-XX:InitiatingHeapOccupancyPercent=45

设置堆占用率的百分比(0到100)达到这个数值的时候触发global concurrent marking(全局并发标记),默认为45%。值为0表示间断进行全局并发标记


-XX:MaxTenuringThreshold=6

设置新生代的对象需要经历多少次GC晋升到老年代的最大阈值。最大值为15,G1中默认值是15


-XX:ParallelGCThreads=8

设置 STW 工作线程数,最多为 8


-XX:ConcGCThreads=4

设置并发标记的线程数。将n设置为并行垃圾回收线程数(ParallelGCThreads)的1/4左右


-XX:G1ReservePercent=10

保留内存区域,防止 to space (Survivor中的to区)溢出。默认值是 10%。增加或减少百分比时,请确保对总的 Java 堆调整相同的量


-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=60

新生代占用整个堆内存的最小百分比(默认5%)、最大百分比(默认60%),这两个是实验性的参数,解锁方式看本文末

注意:避免使用 -Xmn 选项或 -XX:NewRatio 等其他相关选项显式设置年轻代大小。固定年轻代的大小会覆盖暂停时间目标。


-XX:G1MixedGCLiveThresholdPercent=85

设置Old区的region被回收时候的对象占比,默认占用率为85%。只有Old区的region中存活的对象占用达到了这个百分比,才会在Mixed GC中被回收。这是一个实验性的参数,解锁方式看本文末


-XX:G1HeapWastePercent=10

设置你原理浪费的堆的百分比。在global concurrent marking(全局并发标记)结束之后,可以知道所有的Region有多少空间要被回收,在每次young GC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC。默认值10%


-XX:G1MixedGCCountTarget=8

一次global concurrent marking(全局并发标记)之后,最多执行Mixed GC的次数,默认是8。混合回收的目标是要控制在此目标次数以内


-XX:G1OldCSetRegionThresholdPercent=10

设置Mixed GC收集周期中要收集的Old region数的上限。默认值是Java堆的10%


-XX:+ParallelRefProcEnabled

启用并行处理 soft/weak/final/phantom/JNI 引用,建议开启,默认禁用


当您调优混合垃圾回收时,请尝试以下选项 -XX:InitiatingHeapOccupancyPercent 用于更改标记阈值 -XX:G1MixedGCLiveThresholdPercent和-XX:G1HeapWastePercent 当您想要更改混合垃圾回收决定时 -XX:G1MixedGCCountTarget和-XX:G1OldCSetRegionThresholdPercent 当您想要调整Old Region的 CSet 时

GC日志相关参数 #

-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=50m
-Xloggc:/dev/shm/gc.log
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+PrintGCTimeStamps 
-XX:+PrintGCApplicationConcurrentTime 
-XX:+PrintGCApplicationStoppedTime 

详见 G1回收器的GC日志详解

内存溢出自动dump堆内存 #

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/root/logs/{appName}/java_heapdump.hprof

-XX:+HeapDumpOnOutOfMemoryError开启后,当抛出java.lang.OutOfMemoryError的时候,会自动dump堆内存文件到 -XX:HeapDumpPath 参数配置的目录中。用于为后续问题排查保留现场,默认禁用

如何解锁实验性参数 #

在启动参数中的实验性参数之前,需要显示加上-XX:+UnlockExperimentalVMOptions,进行解锁,例如:

java -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=10 -XX:G1MaxNewSizePercent=75 G1test.jar`

注意:

  1. 多个实验性参数只需要加一个 -XX:+UnlockExperimentalVMOptions
  2. -XX:+UnlockExperimentalVMOptions 参数必须加在【最前面的实验性参数】之前

其他参数 #

以上只是常用的一些配置,需要用到更多的话可以参考 官方参数文档