面试必问之JVM常用参数

概述

面试必问系列。

参数分类

  1. 标准参数:-,功能和输出的参数都很稳定,在将来的 JVM 版本中很可能不会改变。用javajava -help命令输出所有的标准参数
  2. 非标准参数:-X,在将来的版本中可能会改变。可用java -X来检索,不保证所有参数都可以被检索出来
  3. 非Stable参数:-XX,种类多,对于布尔类型参数,+表示激活,-表示未激活,注销;非布尔值参数,先写参数名称,然后使用=赋值:-XX:=

常用参数

JVM提供的参数多达几百个,随着JDK版本的迭代,有些参数会被废弃,并且会加入新的参数。参数之间也有依赖关系,即某个参数的开启(生效)需要依赖另一个参数的生效。参数有很多,不可能全部都了解,学习常用的参数即可。

查看当前JVM的默认参数的命令行:java -XX:+PrintCommandLineFlags -version
我的笔记本的输出:

-XX:InitialHeapSize=266555008 -XX:MaxHeapSize=4264880128 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

查看当前JVM支持的参数的命令行:java -XX:+PrintFlagsFinal -version
我的笔记本的输出(有删减):

[Global flags]
     intx AliasLevel                                = 3                                   {C2 product}
     bool BackgroundCompilation                     = true                                {pd product}
     intx CMSAbortablePrecleanWaitMillis            = 100                                 {manageable}
     intx FenceInstruction                          = 0                                   {ARCH product}
     intx InteriorEntryAlignment                    = 16                                  {C2 pd product}
     bool LIRFillDelaySlots                         = false                               {C1 pd product}
     bool PrintHeapAtGC                             = false                               {product rw}
     bool UseCompressedClassPointers               := true                                {lp64_product}
     bool ZeroTLAB                                  = false                               {product}

输出的每一行表示一个XX参数,包括五列:

  1. 第一列:参数的数据类型,有:uintx、intx、bool、ccstr、double、ccstrlist
  2. 第二列:名称
  3. 第三列:=表示第四列是参数的默认值,而:=表明参数被用户或JVM赋值
  4. 第四列:值
  5. 第五列是参数的类别,manageable表示可以动态修改的参数

命令行:java -XX:+PrintFlagsInitial,用于查看参数的初始值(默认值),输出与java -XX:+PrintFlagsFinal -version输出仅第三列不完全一致。

另外,可以指定参数-XX:+UnlockExperimentalVMOptions-XX:+UnlockDiagnosticVMOptions来解锁任何额外的隐藏参数:
输入命令:java -server -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version | grep experimental
输出(有删减):

bool AggressiveUnboxing                        = false                               {C2 experimental}
intx PredictedLoadedClassCount                 = 0                                   {experimental}
intx RTMAbortRatio                             = 50                                  {ARCH experimental}

输入命令:java -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | find "diagnostic"
输出(有删减):

bool BindCMSThreadToCPU                        = false                               {diagnostic}
bool C1PatchInvokeDynamic                      = true                                {C1 diagnostic}

堆、栈、元空间

-Xms:初始堆大小,等价于-XX:InitialHeapSize
-Xmx:最大堆大小,等价于-XX:MaxHeapSize
初始值为JVM启动是向操作系统申请的内存大小(malloc),最大值表示,当使用的内存超过初始值后扩容的最大值。JVM配置多少内存并不是说启动后就会占用多少物理内存,因为操作系统的内存分配是惰性的。对于已申请的内存虽然会分配地址空间,但并不会直接占用物理内存,真正使用时才会映射到实际的物理内存。

-Xmn:设置年轻代大小,相当于同时配置-XX:NewSize-XX:MaxNewSize为同一值,单位默认是字节(不加单位时),一般都会加单位
-XX:NewSize=n:设置年轻代大小(for JDK 1.3/1.4)
-XX:MaxNewSize:设置年轻代最大值(for JDK 1.3/1.4)
-XX:NewRatio=n:默认值为2,设置年轻代和年老代的比值(除去持久代)。如n=3表示年轻代和年老代比值为1:3,年轻代占整个年轻代年老代和的1/4,-Xmn优先级大于-XX:NewRatio。老年代大小无法直接设置,只能通过堆大小+分配比例进行调整,新生代和老年代大小计算方式为:

NewSize = HeapSize / NewRatio + 1
OldSize = (HeapSize / NewRatio + 1 ) * NewRatio

-XX:SurvivorRatio=n:这个比例在Parallel Scavenge(新生代并行回收器,JDK5以后的默认新生代回收器)回收器下是动态的,运行时会出现Eden/Survivor比例和配置的不同。默认值为8,年轻代中Eden区与两个Survivor区的比值。Survivor区有两个。如n=3表示Eden:3 Survivor:2,一个Survivor区占整个年轻代的1/5。计算方式:

SurvivorSize(1) = YoungGenerationSize / (2 + SurvivorRatio)
EdenSize = YoungGenerationSize / (2 + SurvivorRatio) * SurvivorRatio

-XX:PretenureSizeThreshold:当创建的对象超过指定大小时,直接把对象分配在老年代。
-XX:MaxTenuringThreshold:设定对象在Survivor复制的最大年龄阈值,超过阈值转移到老年代

永久代(PermGen,持久代)两个配置参数:
-XX:PermSize:设置永久代的初始大小,如果超出该大小,则会触发垃圾回收。此选项在JDK 8中已弃用,并由-XX:MetaspaceSize选项取代。
-XX:MaxPermSize:设置永久代的最大值,默认是64M
对于永久代,如果动态生成很多class的话,就很可能出现<java.lang.OutOfMemoryError: PermGen space错误>,因为永久代空间配置有限。最典型的场景是,在web开发比较多jsp页面的时候。JDK8之后,方法区存在于元空间(Metaspace)。物理内存不再与堆连续,而是直接存在于本地内存中,理论上机器内存有多大,元空间就有多大。
此选项在JDK 8中已弃用,并由-XX:MaxMetaspaceSize选项取代。

-Xss:每个线程的堆栈大小

-XX:MetaspaceSize:初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放大量的空间,就适当降低该值;如果释放很少的空间,则在不超过MaxMetaspaceSize时,适当提高该值。默认大小取决于平台。

-XX:MaxMetaspaceSize:设置可以分配给Metaspace的最大本机内存,默认是没有大小限制的。应用程序的Metaspace量取决于应用程序本身,其他正在运行的应用程序以及系统上可用的内存量
-XX:MinMetaspaceFreeRatio:在GC后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio:在GC后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

GC

设置使用何种GC:

  • -XX:+UseSerialGC:设置串行收集器
  • -XX:+UseParallelGC:设置并行收集器,等价于-XX:+UseParallelOldGC
  • -XX:+UseParalledlOldGC:设置并行年老代收集器
  • -XX:+UseConcMarkSweepGC:设置CMS收集器
  • -XX:+UseG1GC:使用G1收集器

一般都并行GC,常用参数

  • -XX:ParallelGCThreads=n:设置并行收集器年轻代收集方式为并行收集时,使用的CPU数,并行收集线程数
  • -XX:MaxGCPauseMillis=n:设置并行收集最大的暂停时间(如果到这个时间,GC依然没有回收完,也会停止回收)
  • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比,公式为:1/(1+n)
  • -XX:+CMSIncrementalMode:设置为增量模式,适用于单CPU

-XX:CMSFullGCsBeforeCompaction=5:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生碎片,使得运行效率降低。此值设置运行5次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但可以消除碎片

打印GC回收过程的日志信息,不要用-XX:+UseGCLogFileRotation-XX:NumberOfGCLogFiles=5-XX:GCLogFileSize=20M,会丢失旧的日志文件,而且重启会覆盖当前日志文件。

  • -XX:+PrintGC
  • -XX:+PrintGCDetails:依赖于PrintGC
  • -XX:+PrintGCTimeStamps:依赖于PrintGC
  • -Xloggc:/home/GCEASY/gc-%t.log

-XX:MAXTenuringThrehold:每个对象都有一个计数器,每次进行YGC时,都会 +1。通过此参数可以配置当计数器的值到达某个阈值时,对象就会从新生代移送至老年代。
-XX:InitiatingHeapOccupancyPercent:启动并发GC周期时堆内存使用占比,G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比。值为 0 则表示一直执行GC循环,默认值45。
-XX:G1HeapWastePercent:允许的浪费堆空间的占比,默认是10%,如果并发标记可回收的空间小于10%,则不会触发MixedGC。
-XX:ConcGCThreads=n:并发垃圾收集器使用的线程数量,默认值随JVM运行的平台不同而不同
-XX:G1MixedGCLiveThresholdPercent=65:混合垃圾回收周期中要包括的旧区域设置占用率阈值,默认占用率为65%
-XX:G1MixedGCCountTarget=8:设置标记周期完成后,对存活数据上限为 G1MixedGCLIveThresholdPercent 的旧区域执行混合垃圾回收的目标次数默认8次混合垃圾回收,混合回收的目标是要控制在此目标次数以内
-XX:G1OldCSetRegionThresholdPercent=1:描述Mixed GC时,Old Region被加入到CSet中。默认情况下,G1只把10%的Old Region加入到CSet中。
-XX:PrintTenuringDistribution:打印tenuring年龄信息,

PrintHeapAtGC:一般都是false,即关闭。

JMX

-Djava.net.preferIPv4Stack=true
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname={hostname}
-Dcom.sun.management.jmxremote.port={port}
-Dcom.sun.management.jmxremote.authenticate=false

其他

打印ClassLoader日志(所有类加载/卸载信息)
-XX:+TraceClassLoading
-XX:+TraceClassUnloading

OOM时Dump内存
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/heap-dump.hprof

OOM时执行脚本(比如重启)
-XX:OnOutOfMemoryError=/scripts/restart-myapp.sh

禁止系统System.gc(),防止手动误触发FGC造成问题
-XX:+DisableExplicitGC

查看TLAB空间的使用情况
-XX:+PrintTLAB

打印JIT时间
-XX:-CITime

方法被编译时打印相关信息
-XX:-PrintCompilation

设置Integer缓存区间,默认值128,表示[-127, 128]
-XX:AutoBoxCacheMax=256

-XX:+PrintCompilation不能输出详细的信息,开启此参数把扩展的编译输出到hotspot.log文件。需解锁实验性功能。
+LogCompilation

由于与吞吐量关系密切,PS收集器也经常称为吞吐量优先收集器。-XX:+UseAdaptiveSizePolicy是一个开关参数,当这个参数打开之后,就不需要手工指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数,VM会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)。使用PS收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成将是一个不错的选择。只需要把基本的内存数据设置好(如-Xmx设置最大堆),然后使用MaxGCPauseMillis参数(更关注最大停顿时间)或GCTimeRatio (更关注吞吐量)参数给虚拟机设立一个优化目标,那具体细节参数的调节工作就由虚拟机完成。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。

新生代使用ParNew,老年代使用Serial Old
-XX:+UseParNewGC(在Java8中已弃用,在Java9中已删除)

最佳实践

下面是一些缺省的写法
JDK7

JAVA_MEM_OPTS="-server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70"
JAVA_DEBUG_OPTS="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof -XX:OnOutOfMemoryError=/scripts/restart-myapp.sh"

JDK8

JAVA_MEM_OPTS="-server -Xmx2g -Xms2g -Xmn256m -XX:MetaspaceSize=256m -Xss1024m -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70"
JAVA_DEBUG_OPTS="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof -XX:OnOutOfMemoryError=/scripts/restart-myapp.sh"

JDK8中已经支持G1。G1为用户的应用程序的提供一个低GC延时和大内存GC的解决方案,适用于大内存场景(官方推荐堆6G以上)。如果程序正在使用CMS或ParallelOld垃圾回收器,并具有以下一个或多个特征,则可以考虑升级为G1:

  • Full GC持续时间太长或太频繁
  • 对象分配率或年轻代升级老年代很频繁
  • 垃圾收集时间或压缩暂停(超过0.5至1秒)时间过长

PS:如果正在使用CMS或ParallelOld收集器,并且程序没有遇到长时间的垃圾收集暂停,那么就不需要升级到G1。

动态修改参数

不是所有的参数都可以动态修改,查看哪些参数可动态修改:
java -XX:+PrintFlagsFinal | grep manageable

参考

posted @ 2022-09-07 18:53  johnny233  阅读(48)  评论(0编辑  收藏  举报  来源