04性能监控与调优篇(D4_JVM参数)
目录
一、标准参数
二、非标准参数
三、不稳定参数
四、常⽤参数
-XX:+PrintFlagsFinal
-XX:+PrintCommandLineFlags
-XX:CMSFullGCsBeforeCompaction
-XX:HeapDumpPath
-XX:OnOutOfMemoryError
XX:InitialCodeCacheSize
-XX:+UseCodeCacheFlushing
四、GC⽇志相关参数
开启GC⽇志
开启GC时间提示
开启⽇志滚动输出
开启语句
其他有用参数
日志含义
五、其他实用参数
在JVM调整过程中,主要是对JVM参数做的调整,以下我们介绍主要参数。JVM参数有很多,其实我 们直接使⽤默认的JVM参数,
不去修改都可以满⾜⼤多数情况。但是如果你想在有限的硬件资源下,部 署的系统达到最⼤的运⾏效率,那么进⾏相关的JVM参数设置是
必不可少的。下⾯我们就来对这些JVM参数进⾏详细的介绍。
关于这些命令的详细解释,可以参考官⽹:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
JVM参数主要分为以下三种:标准参数、⾮标准参数、不稳定参数。
一、标准参数
标准参数,顾名思义,标准参数中包括功能以及输出的结果都是很稳定的,基本上不会随着JVM版 本的变化⽽变化。
标准参数以-开头,如:java -version、java -jar等,通过java -help可以查询所有的标 准参数,我们可以通过 -help 命令来检索出所有标
准参数。
-help 也是⼀个标准参数,再⽐如使⽤⽐较多的 -version也是。
显示Java的版本信息。
二、非标准参数
⾮标准参数以-X开头,是标准参数的扩展。对应前⾯讲的标准化参数,这是⾮标准化参数。表示在 将来的JVM版本中可能会发⽣改变,
但是这类以-X开始的参数变化的⽐较⼩。
我们可以通过 Java -X 命令来检索所有-X 参数。
我们可以通过设置⾮标准参数来配置堆的内存分配,常⽤的⾮标准参数有:
-Xmn新⽣代内存的最⼤值,包括Eden区和两个Survivor区的总和,写法如:-Xmn1024,- Xmn1024k,-Xmn1024m,-Xmn1g 。
-Xms堆内存的最⼩值,默认值是总内存/64(且⼩于1G),默认情况下,当堆中可⽤内存⼩于40%
(这个值可以⽤-XX: MinHeapFreeRatio 调整,如-X:MinHeapFreeRatio=30)时,堆内存会开始增加,⼀ 直增加到-Xmx的⼤⼩。
-Xmx堆内存的最⼤值,默认值是总内存/64(且⼩于1G),如果Xms和Xmx都不设置,则两者⼤⼩ 会相同,默认情况下,当堆中可⽤
内存⼤于70%时,堆内存会开始减少,⼀直减⼩到-Xms的⼤⼩;
-Xss每个线程的栈内存,默认1M,⼀般来说是不需要改的。
-Xrs减少JVM对操作系统信号的使⽤。
-Xprof跟踪正运⾏的程序,并将跟踪数据在标准输出输出;适合于开发环境调试。
-Xnoclassgc关闭针对class的gc功能;因为其阻⽌内存回收,所以可能会导致OutOfMemoryError错误,慎⽤;
-Xincgc开启增量gc(默认为关闭);这有助于减少⻓时间GC时应⽤程序出现的停顿;但由于可能 和应⽤程序并发执⾏,所以会降低
CPU对应⽤的处理能⼒。
-Xloggc:file与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到⼀个⽂件中,⽂件的位 置最好在本地,以避免⽹络的潜在问
题。
三、不稳定参数
这是我们⽇常开发中接触到最多的参数类型。这也是⾮标准化参数,相对来说不稳定,随着JVM版 本的变化可能会发⽣变化,主要⽤于
JVM调优和debug。
不稳定参数以-XX 开头,此类参数的设置很容易引起JVM 性能上的差异,使JVM存在极⼤的不稳定 性。如果此类参数设置合理将⼤
⼤提⾼JVM的性能及稳定性。
不稳定参数分为三类:
- 性能参数:⽤于JVM的性能调优和内存分配控制,如内存⼤⼩的设置;
- 行为参数:⽤于改变JVM的基础⾏为,如GC的⽅式和算法的选择;
- 调试参数:⽤于监控、打印、输出jvm的信息;
不稳定参数语法规则:
布尔类型参数值:
- -XX:+
- -XX:-示例:-XX:+UseG1GC:表示启⽤G1垃圾收集器
数字类型参数值:
- -XX:示例:-XX:MaxGCPauseMillis=500 :表示设置GC的最⼤停顿时间是500ms
字符串类型参数值:
- -XX:示例:-XX:HeapDumpPath=./dump.core
四、常⽤参数
如以下参数示例
–Xms4g -Xmx4g –Xmn1200m –Xss512k -XX:NewRatio=4 -XX:SurvivorRatio=8 -
XX:PermSize=100m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=15 -
XX:MaxDirectMemorySize=1G -XX:+DisableExplicitGC
上⾯为Java7及以前版本的示例,在Java8中永久代的参数-XX:PermSize和-XX:MaxPermSize已经失 效。
参数解析:
-Xms4g:初始化堆内存⼤⼩为4GB,ms是memory start的简称,等价于-XX:InitialHeapSize。
-Xmx4g:堆内存最⼤值为4GB,mx是memory max的简称,等价于-XX:MaxHeapSize。
-Xmn1200m:设置年轻代⼤⼩为1200MB。增⼤年轻代后,将会减⼩年⽼代⼤⼩。此值对系统性 能影响较⼤,Sun官⽅推荐配置为整个堆的3/8。
-Xss512k:设置每个线程的堆栈⼤⼩。JDK5.0以后每个线程堆栈⼤⼩为1MB,以前每个线程堆栈 ⼤⼩为256K。应根据应⽤线程所需内存
⼤⼩进⾏调整。在相同物理内存下,减⼩这个值能⽣成更 多的线程。但是操作系统对⼀个进程内的线程数还是有限制的,不能⽆限⽣
成,经验值在3000~5000左右。
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年⽼代的⽐值(除去持久代)。 设置为4,则年轻代与年⽼代所占⽐值为1:4,年轻代占整个堆栈的1/5 -XX:SurvivorRatio=8:设置年轻代中Eden区与Survivor区的⼤⼩⽐值。设置为8,则两个Survivor
区与⼀个Eden区的⽐值为2:8,⼀个Survivor区占整个年轻代的1/10 -XX:PermSize=100m:初始化永久代⼤⼩为100MB。
-XX:MaxPermSize=256m:设置持久代⼤⼩为256MB。
-XX:MaxTenuringThreshold=15:设置垃圾最⼤年龄。如果设置为0的话,则年轻代对象不经过
Survivor区,直接进⼊年⽼代。对于年⽼代⽐较多的应⽤,可以提⾼效率。如果将此值设置为⼀个 较⼤值,则年轻代对象会在Survivor区进⾏多次复制,这样可以增加对象再年轻代的存活时间,增 加在年轻代即被回收的概论。
-XX:MaxDirectMemorySize=1G:直接内存。报java.lang.OutOfMemoryError: Direct buffer memory异常可以上调这个值。
-XX:+DisableExplicitGC:禁⽌运⾏期显式地调⽤System.gc()来触发fulll GC。
注意: Java RMI的定时GC触发机制可通过配置-Dsun.rmi.dgc.server.gcInterval=86400来控制 触发的时间。
-XX:CMSInitiatingOccupancyFraction=60:⽼年代内存回收阈值,默认值为68。
-XX:ConcGCThreads=4:CMS垃圾回收器并⾏线程线,推荐值为CPU核⼼数。
-XX:ParallelGCThreads=8:新⽣代并⾏收集器的线程数。
-XX:MaxTenuringThreshold=10:设置垃圾最⼤年龄。如果设置为0的话,则年轻代对象不经过
Survivor区,直接进⼊年⽼代。对于年⽼代⽐较多的应⽤,可以提⾼效率。如果将此值设置为⼀个 较⼤值,则年轻代对象会在Survivor区进⾏多次复制,这样可以增加对象再年轻代的存活时间,增 加在年轻代即被回收的概论。
-XX:CMSMaxAbortablePrecleanTime=500:当abortable-preclean预清理阶段执⾏达到这个时间 时就会结束。
更多详细参数说明请参考官⽅⽂档:
Java HotSpot VM Options
新⽣代、⽼⽣代、永久代的参数,如果不进⾏指定,虚拟机会⾃动选择合适的值,同时也会 基于系统的开销⾃动调整。
-XX:+PrintFlagsFinal
Java 6(update 21oder 21之后)版本, HotSpot JVM 提供给了两个新的参数,在JVM启动后,在 命令⾏中可以输出所有XX参数和值。
-XX:+PrintFlagsInitial:查看初始值
-XX:+PrintFlagsFinal:查看最终值(初始值可能被修改掉)
让我们现在就了解⼀下新参数的输出。以 -client 作为参数的 -XX:+ PrintFlagsFinal 的结果是⼀ 个按字⺟排序的590个参数表格(注意,每个release版本参数的数量会不⼀样)
表格的每⼀⾏包括五列,来表示⼀个XX参数。第⼀列表示参数的数据类型,第⼆列是名称,第四列 为值,第五列是参数的类别。
第三列”=”表示第四列是参数的默认值,⽽”:=” 表明了参数被⽤户或者JVM赋值了。
如果我们只想看下所有XX参数的默认值,能够⽤⼀个相关的参数,-XX:+PrintFlagsInitial 。
⽤ - XX:+PrintFlagsInitial , 只是展示了第三列为“=”的数据(也包括那些被设置其他值的参数)。
然⽽,注意当与-XX:+PrintFlagsFinal 对⽐的时候,⼀些参数会丢失,⼤概因为这些参数是动态创建的。
-XX:+PrintCommandLineFlags
让我们看下这个参数,事实上这个参数⾮常有⽤: -XX:+PrintCommandLineFlags 。这个参数让
JVM打印出那些已经被⽤户或者JVM设置过的详细的XX参数的名称和值。
换句话说,它列举出 -XX:+PrintFlagsFinal的结果中第三列有":="的参数。以这种⽅式, 我们可 以⽤-XX:+PrintCommandLineFlags作为
快捷⽅式来查看修改过的参数。看下⾯的例⼦。
现在如果我们每次启动java 程序的时候设置 -XX:+PrintCommandLineFlags 并且输出到⽇志⽂件上,这 样会记录下我们设置的JVM 参数
对应⽤程序性能的影响。
-XX:CMSFullGCsBeforeCompaction
CMSFullGCsBeforeCompaction 说的是,在上⼀次CMS并发GC执⾏过后,到底还要再执⾏多少次
full GC才会做压缩。默认是0,也就是在默认配置下每次CMS GC顶不住了⽽要转⼊full GC的时候都会 做压缩。
如果把CMSFullGCsBeforeCompaction配置为10,就会让上⾯说的第⼀个条件变成每隔10次 真正的full GC才做⼀次压缩(⽽不是每10
次CMS并发GC就做⼀次压缩,⽬前VM⾥没有这样的参数)。 这会使full GC更少做压缩,也就更容易使CMS的old gen受碎⽚化问题的困
扰。 本来这个参数就是⽤来 配置降低full GC压缩的频率,以期减少某些full GC的暂停时间。CMS回退到full GC时⽤的算法是
marksweep-compact,但compaction是可选的,不做的话碎⽚化会严重些但这次full GC的暂停时间会短 些;这是个取舍。
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=10
两个参数必须同时使⽤才能⽣效。
-XX:HeapDumpPath
堆内存出现OOM的概率是所有内存耗尽异常中最⾼的,出错时的堆内信息对解决问题⾮常有帮 助,所以给JVM设置这个参数
(-XX:+HeapDumpOnOutOfMemoryError),让JVM遇到OOM异常时能 输出堆内信息,并通过(-XX:+HeapDumpPath)
参数设置堆内存溢出快照输出的⽂件地址,这对于 特别是对相隔数⽉才出现的OOM异常尤为重要。
这两个参数通常配套使⽤:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
-XX:OnOutOfMemoryError
-XX:OnOutOfMemoryError="/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/binjconsole"
表示发⽣OOM后,运⾏jconsole程序。这⾥可以不⽤加“”,因为jconsole.exe路径Program Files含 有空格。
利⽤这个参数,我们可以在系统OOM后,⾃定义⼀个脚本,可以⽤来发送邮件告警信息,可以⽤ 来重启系统等等。
XX:InitialCodeCacheSize
JVM⼀个有趣的,但往往被忽视的内存区域是“代码缓存”,它是⽤来存储已编译⽅法⽣成的本地代 码。
代码缓存确实很少引起性能问题,但是⼀旦发⽣其影响可能是毁灭性的。如果代码缓存被占满,
JVM会打印出⼀条警告消息,并切换到interpreted-only 模式:JIT编译器被停⽤,字节码将不再会被编 译成机器码。
因此,应⽤程序将继续运⾏,但运⾏速度会降低⼀个数量级,直到有⼈注意到这个问题。 就像其他内存区域⼀样,我们可以⾃定义代码
缓存的⼤⼩。
相关的参数是-XX:InitialCodeCacheSize 和XX:ReservedCodeCacheSize,它们的参数和上⾯介绍的参数⼀样,都是字节值。
-XX:+UseCodeCacheFlushing
如果代码缓存不断增⻓,例如,因为热部署引起的内存泄漏,那么提⾼代码的缓存⼤⼩只会延缓其 发⽣溢出。为了避免这种情况的发
⽣,我们可以尝试⼀个有趣的新参数:当代码缓存被填满时让JVM放 弃⼀些编译代码。通过使⽤-XX:+UseCodeCacheFlushing 这个参
数,我们⾄少可以避免当代码缓存被 填满的时候JVM切换到interpreted-only 模式。不过,我仍建议尽快解决代码缓存问题发⽣的根本原
因,如找出内存泄漏并修复它。
四、GC⽇志相关参数
设置JVM GC格式⽇志的主要参数包括如下8个:
- -XX:+PrintGC 输出简要GC⽇志
- -XX:+PrintGCDetails 输出详细GC⽇志
- -Xloggc:gc.log 输出GC⽇志到⽂件
- -XX:+PrintGCTimeStamps 输出GC的时间戳(以JVM启动到当期的总时⻓的时间戳形式)
- -XX:+PrintGCDateStamps 输出GC的时间戳(以⽇期的形式,如 2020-04- 26T21:53:59.234+0800)
- -XX:+PrintHeapAtGC 在进⾏GC的前后打印出堆的信息
- -verbose:gc : 在JDK 8中,-verbose:gc是-XX:+PrintGC⼀个别称,⽇志格式等价与:- XX:+PrintGC。不过在JDK 9中 -XX:+PrintGC被标记为deprecated。
- -verbose:gc是⼀个标准的选项,-XX:+PrintGC是⼀个实验的选项,建议使⽤-verbose:gc 替代XX:+PrintGC 8. -XX:+PrintReferenceGC 打印年轻代各个引⽤的数量以及时⻓
开启GC⽇志
多种⽅法都能开启GC的⽇志功能,其中包括:使⽤-verbose:gc或-XX:+PrintGC这两个标志中的任 意⼀个能创建基本的GC⽇志(这两个
⽇志标志实际上互为别名,默认情况下的GC⽇志功能是关闭的) 使⽤-XX:+PrintGCDetails标志会创建更详细的GC⽇志
推荐使⽤-XX:+PrintGCDetails标志(这个标志默认情况下也是关闭的);通常情况下使⽤基本的
GC⽇志很难诊断垃圾回收时发⽣的问题。
开启GC时间提示
除了使⽤详细的GC⽇志,我们还推荐使⽤-XX:+PrintGCTimeStamps或者XX:+PrintGCDateStamps,便于我们更精确地判断⼏次GC操作
之间的时间。这两个参数之间的差别在 于时间戳是相对于0(依据JVM启动的时间)的值,⽽⽇期戳(date stamp)是实际的⽇期字符
串。由 于⽇期戳需要进⾏格式化,所以它的效率可能会受轻微的影响,不过这种操作并不频繁,它造成的影响 也很难被我们感知。
指定GC⽇志路径
默认情况下GC⽇志直接输出到标准输出,不过使⽤-Xloggc:filename标志也能修改输出到某个⽂ 件。除⾮显式地使⽤-PrintGCDetails标
志,否则使⽤-Xloggc会⾃动地开启基本⽇志模式。 使⽤⽇志循环(Log rotation)标志可以限制保存在GC⽇志中的数据量; 对于需要⻓
时间运⾏的服务器⽽⾔,这是⼀个⾮常有⽤的标志,否则累积⼏个⽉的数据很可能会耗尽服 务器的磁盘。
开启⽇志滚动输出
通过-XX:+UseGCLogfileRotation -XX:NumberOfGCLogfiles=N -XX:GCLogfileSize=N标志可以控制 ⽇志⽂件的循环。
默认情况下,UseGCLogfileRotation标志是关闭的。它负责打开或关闭GC⽇志滚动记录功能的。 要求必须设置 -Xloggc参数 开启
UseGCLogfileRotation标志后,默认的⽂件数⽬是0(意味着不作任何限制),默认的⽇志⽂件⼤ ⼩是0(同样也是不作任何限制)。
因此,为了让⽇志循环功能真正⽣效,我们必须为所有这些标志设定值。 需要注意的是:
The size of the log file at which point the log will be rotated, must be >= 8K. 设置滚动⽇志⽂件 的⼤⼩,必须⼤于8k。 当前写⽇志⽂
件⼤⼩超过该参数值时,⽇志将写⼊下⼀个⽂件 设置滚动⽇志⽂件的个数,必须⼤于等于1
必须设置 -Xloggc 参数
开启语句
-XX:+PrintGCDetails -XX:+PrintGCDateStamps
-Xloggc:/home/hadoop/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=512k
其他有用参数
-XX:+PrintGCApplicationStoppedTime 打印GC造成应⽤暂停的时间
-XX:+PrintTenuringDistribution 在每次新⽣代 young GC时,输出幸存区中对象的年龄分布