面试必问之JVM常用参数
概述
面试必问系列。
参数分类
- 标准参数:
-
,功能和输出的参数都很稳定,在将来的 JVM 版本中很可能不会改变。用java
或java -help
命令输出所有的标准参数 - 非标准参数:
-X
,在将来的版本中可能会改变。可用java -X
来检索,不保证所有参数都可以被检索出来 - 非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
参数,包括五列:
- 第一列:参数的数据类型,有:uintx、intx、bool、ccstr、double、ccstrlist
- 第二列:名称
- 第三列:
=
表示第四列是参数的默认值,而:=
表明参数被用户或JVM赋值 - 第四列:值
- 第五列是参数的类别,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