概述
面试必问系列。
参数分类
- 标准参数:
-,功能和输出的参数都很稳定,在将来的 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
参考
- JVM常用参数以及命令
- 动态修改JVM参数










