TIPS 本文基于JDK 11编写,理论支持JDK 8及更高版本。 部分参数的默认值可能有偏差(笔者是在macOS下测试的,但不代表所有操作系统下都是该值),读者可以

  1. java -XX:+PrintFlagsInitial

查看本机的初始化参数。

堆(Heap)

TIPS

  • 其中,Xms、Xmx的值不同时,JVM会初始分配Xms大小的堆内存,如果发现内存不够用时,会自动扩容,直到达到Xmx。例如-Xms128m -Xmx1g,那么,堆内存初始分配128M,如果发现可用空间低于阈值(-XX:MinHeapFreeRatio),则自动扩容,直到达到Xmx。建议生产环境将Xms和Xmx设成相同值。
  • TLAB相关参数建议用默认值即可。 | 属性 | 作用 | 默认值 | | —- | —- | —- | | -Xmn | 设置年轻代的初始值及最大值,相当于将-XX:NewSize、-XX:MaxNewSize设置成相同的值。如果该区域设置太小,则会执行大量Minor GC,如果设置太大,则可能Full GC满了,年轻代还没满。Oracle建议将年轻代设置在堆大小的25%-50%之间。(有些文章说建议设置成堆的3/8,我不知道是哪儿找的),笔者参考的是 https://docs.oracle.com/en/java/javase/11/tools/java.html | - | | -Xms | 设置堆内存的初始大小,相当于-XX:InitialHeapSize | 0 | | -Xmx | 设置堆内存的最大大小,相当于-XX:MaxHeapSize | - | | -XX:NewRatio | 老年代和年轻代的比值 | 2 | | -XX:SurvivorRatio | Eden和1个Survivor区的比值 | 8 | | -XX:InitialSurvivorRatio | 初始Eden和1个Survivor区的比值 | 8 | | -XX:MinSurvivorRatio | Eden和1个Survivor区的最小比值 | 3 | | -XX:TargetSurvivorRatio | 设置年轻代垃圾回收后,期望的Survivor区内存使用比值。(Sets the desired percentage of survivor space (0 to 100) used after young garbage collection. 翻译可能不够贴切,原文也贴出来吧,意会一下) | 50 | | -XX:OldSize | 初始老年代大小 | - | | -XX:InitialTenuringThreshold | 晋升到老年代的对象的初始年龄阈值 | 7 | | -XX:MaxTenuringThreshold | 晋升到老年代的对象的最大年龄阈值。如果设置成0,则年轻代对象不经过Survivor区,直接进入年老代。 | 15 | | -XX:MinHeapFreeRatio | GC后,如果空闲内存小于该值时,JVM会增大分配的堆内存。 | 40 | | -XX:MaxHeapFreeRatio | GC后,如果空闲内存大于此值时,JVM会减少分配的堆内存。 | 70 | | -XX:+ShrinkHeapInSteps | 是否要逐步地根据–XX:MaxHeapFreeRatio的设置,减少分配的堆内存。默认情况下启用该选项,如禁用该参数,那么将会在下一个Full GC时将Java堆直接减少到目标大小,而无需在多个GC周期中“逐步”减少。因此,如果想要让使用的堆内存尽量小,可禁用此选项。 | 启用 | | -XX:SoftRefLRUPolicyMSPerMB | 每兆堆空闲空间中SoftReference的存活时间,单位毫秒 | 1000 | | -XX:+UseTLAB | 是否启用线程私有分配缓存区(thread-local allocation buffer) | 启用 | | -XX:MinTLABSize | 最小TLAB大小,单位字节 | 2048 | | -XX:+ResizeTLAB | 是否动态调整TLAB的大小 | 是 | | -XX:TLABRefillWasteFraction | 由于TLAB空间比较小,因此很容易装满。比如TLAB 100K,已使用80KB,当需要再分配一个30KB的对象时,就无法分配到这个TLAB了。这时虚拟机会有两种选择,第一,废弃当前TLAB,这样就会浪费20KB空间;第二,保留当前的TLAB并将这30KB的对象直接分配在堆上,这样将来有小于20KB的对象时,仍可使用这块空间。实际上虚拟机内部维护了一个叫作refill_waste的值,当请求对象大于refill_waste时,会在堆中分配;若小于该值,则会废弃当前TLAB,新建TLAB分配对象。TLABRefillWasteFraction来调整该阈值,它表示TLAB中允许产生这种浪费的比例,默认值为64,即允许使用1/64的TLAB空间作为refill_waste。默认情况下,TLAB和refill_waste都会在运行时不断调整,使系统的运行状态达到最优。如果想要禁用自动调整TLAB的大小,可以使用-XX:-ResizeTLAB禁用ResizeTLAB,并使用-XX:TLABSize手工指定一个TLAB的大小。 | 64 | | -XX:+TLABStats | 是否提供详细的TLAB的统计信息 | 是 | | -XX:TLABSize | 设置TLAB的初始大小。如果设置成0,JVM会自动设置TLAB初始大小。 | 0 | | -XX:TLABWasteTargetPercent | 允许TLAB占用Eden空间百分比 | 1 |

栈(Stack)

属性 作用 默认值
-Xss 设置线程栈大小,等价于-XX:ThreadStackSize 不同操作系统下默认值不同。

元空间(Metaspace)

TIPS

  • 元空间从JDK 8开始引入。对于JDK 7及更低版本,可使用-XX:PermSize、-XX:MaxPermSize分别指定初始永久代大小、最大永久代大小。
  • 元空间满了会导致Full GC。建议通过jstat等工具观察元空间的使用情况,并设置一个预留的值。 | 属性 | 作用 | 默认值 | | —- | —- | —- | | -XX:MetaspaceSize | 元空间的初始值,元空间占用达到该值就会触发垃圾收集,进行类型卸载,同时,收集器会自动调整该值。如果能够释放空间,就会自动降低该值;如果释放空间很少,那么在不超过-XX:MaxMetaspaceSize的情况下,可适当提高该值。 | 21810376字节 | | -XX:MaxMetaspaceSize | 元空间最大值 | 受限于本地内存大小 | | -XX:MinMetaspaceFreeRatio | 垃圾收集后,计算当前元空间的空闲百分比,如果小于该值,就增加元空间的大小 | 40% | | -XX:MaxMetaspaceFreeRatio | 垃圾收集后,计算当前元空间的空闲百分比,如果大于该值,就减少元空间的大小 | 70% | | -XX:MinMetaspaceExpansion | 元空间增长时的最小幅度 | 340784字节 | | -XX:MaxMetaspaceExpansion | 元空间增长时的最大幅度 | 5452592字节 |

直接内存(Direct Memory)

属性 作用 默认值
-XX:MaxDirectMemorySize 设置最大直接内存大小 0

代码缓存(Code Cache)

属性 作用 默认值
-XX:InitialCodeCacheSize 设置代码缓存区的初始大小,以 java -XX:+PrintFlagsFinal | grep InitialCodeCacheSize 结果为准 不同操作系统、不同编译器的值不同
-XX:ReservedCodeCacheSize 设置代码缓存区的最大大小,以 java -XX:+PrintFlagsFinal | grep ReservedCodeCacheSize 结果为准 不同版本不同,JDK 8 64位、JDK 11 64位都是240M
-XX:-PrintCodeCache 在JVM停止时打印代码缓存的使用情况 关闭
-XX:-PrintCodeCacheOnCompilation 每当方法被编译后,就打印一下代码缓存区的使用情况 关闭
-XX:+UseCodeCacheFlushing 代码缓存区即将耗尽时,尝试回收一些早期编译、很久未被调用的方法 打开
-XX:-SegmentedCodeCache 是否使用分段的代码缓存区,默认关闭,表示使用整体的代码缓存区 关闭

调优的不可能三角

  • 内存占用(Footprint)
  • 吞吐量(Throughput)
  • 延迟(Latency)

比如,对于特定软件,GC要回收的垃圾总量基本是确定的。那么要想回收垃圾,提高GC的频率,这样每次GC的时间就比较少;要么少做几次GC,每次多停顿一些时间。
又比如,而为了降低延迟,CMS/G1采用了并发收集,并发阶段GC线程和业务线程同时运行。但这样做,并发阶段GC线程和业务线程互相都有影响,吞吐量会有所降低。
因此,以上三者最多只能选其二。

调优示例

示例1:如下配置可获得较高的吞吐量:

  1. java -server -XX:+UseParallelGC -XX:+UseLargePages -Xmn10g -Xms26g -Xmx26g

示例2:如下配置可获得较低的延迟:

  1. java -XX:+UseG1GC -Xms26g Xmx26g -XX:MaxGCPauseMillis=500 -XX:+PrintGCTimeStamp

示例3:如下配置可让堆内存比较小,并可减少动态内存分配的占用:

  1. -XX:MaxHeapFreeRatio=10 -XX:MinHeapFreeRatio=5
  2. # 默认情况下这两个值是70和40。空闲内存>70%才会收缩堆;<40%才会扩展堆。意味着大量的内存都是动态分配的。

参考文档