Pre
如题, 一顿操作猛如虎,业务系统总算快发布了,可是JVM的参数还是当初随意设置的那么几个参数, 咋弄? 系统的流量预估(均值、峰值)导致一系列的评估: 每秒的对象生成大小,新生代 老年代的比例是否合理, 动态年龄判断机制、老年代担保机制会不会被频繁触发,full gc 的频率。。。。
留下一脸懵逼的你在风中瑟瑟发抖~
不要怕 ,今天我们就来看一下如何通过合理的预估来设置系统的JVM参数
JVM-10虚拟机性能监控与故障处理工具之【JDK的命令行】
名称 | 英文全称 | 作用 |
---|---|---|
jps | JVM Process Status Tool | 显示指定系统内所有的HotSPot虚拟机进程 |
jstat | JVM Statistics Monitoring Tool | 用于手机HotSpot虚拟机个方面的运行数据 |
jinfo | Configuration Info for Java | 显示虚拟机配置信息 |
jmap | Memory Map for Java | 生成虚拟机的内存转储快照(headump文件) |
jhat | Java Heap Analysis Tool | 虚拟机堆转储快照分析工具 |
jstack | Stack Trace For Java | java堆栈跟踪工具,,显示线程快照 |
jstat
代码语言:javascript复制[root@artisan ~]# java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
[root@artisan ~]# jstat -help
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Definitions:
<option> An option reported by the -options option
<vmid> Virtual Machine Identifier. A vmid takes the following form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id; <hostname> is
the name of the host running the target Java virtual machine;
and <port> is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
<lines> Number of samples between header lines.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
<count> Number of samples to take before terminating.
-J<flag> Pass <flag> directly to the runtime system.
[root@artisan ~]#
官方都说了 jstat -help|-options ,是吧 看看 options的选项吧
代码语言:javascript复制[root@artisan ~]# jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation
[root@artisan ~]#
从中可以看出 , jstat (JVM Statistics Monitoring Tool)用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾回收、JIT编译等运行数据 。
划重点, 垃圾回收 ,这里我们重点看GC垃圾回收 ,因为JVM调优的本质就是为了减少full GC 的发生。
通过不同方式的测试,观察GC的情况,可以很好的预测和分析 每秒的对象生成大小,新生代 老年代的比例是否合理, 动态年龄判断机制、老年代担保机制会不会被频繁触发,full gc 的频率。。。。
选项option代表用户希望查询的虚拟机信息,主要分为3类:类装载、垃圾收集、运行期编译状况。
jstat的主要选项
选项 | 作用 |
---|---|
-class | 监视类装载、卸载数量,总空间以及类装载锁耗费的时间 |
-gc | 监视Java堆状况,包括eden区、两个Survivor区、老年代、永久代等的容量、已使用空间、GC时间合计等信息 |
-gccapacity | 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用的最大、最小空间 |
-gcutil | 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比 |
-gccause | 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因 |
-gcnew | 监视新生代GC状况 |
-gcnewcapacity | 监视内容与-gcnew 基本相同,但输出主要关注Java堆各个区域使用的最大、最小空间 |
-gcold | 监视老年代代GC状况 |
-gcoldcapacity | 监视内容与-gcold 基本相同,但输出主要关注Java堆各个区域使用的最大、最小空间 |
-gcpermcapacity | 输出永久代使用的最大、最小空间 |
-compiler | 输出JIT编译器编译过的方法、耗时等信息 |
-printcompilation | 输出已经被JIT编译的方法 |
评估内存使用及GC压力的整体情况 jstat -gc PID
代码语言:javascript复制[root@artisan ~]# jps
5811 kooteam.jar
10478 Jps
[root@artisan ~]# jstat -gc 5811
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
34944.0 34944.0 0.0 0.0 279616.0 44044.5 699072.0 13979.2 21248.0 20630.5 2560.0 2386.8 0 0.000 1 0.338 0.338
[root@artisan ~]#
参数解读
- S0C:第一个幸存区的大小,单位KB
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法区大小(元空间)
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间,单位s
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间,单位s
- GCT:垃圾回收消耗总时间,单位s
当然了,看一次肯定不行啊,一般都是间隔多长时间输出一次,持续观察。
比如 间隔2秒,打印1万次
代码语言:javascript复制[root@artisan ~]# jstat -gc 5811 2000 10000
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
34944.0 34944.0 0.0 0.0 279616.0 56627.5 699072.0 13979.2 21248.0 20630.5 2560.0 2386.8 0 0.000 1 0.338 0.338
34944.0 34944.0 0.0 0.0 279616.0 56627.5 699072.0 13979.2 21248.0 20630.5 2560.0 2386.8 0 0.000 1 0.338 0.338
根据压测,结合这个GC各个分代的情况,加之对业务的理解,推断是否存在不合理的地方。
-gcutil 输出主要关注已使用空间占总空间的百分比
监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
代码语言:javascript复制[root@artisan ~]# jstat -gcutil 5811
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 24.75 2.00 97.09 93.23 0 0.000 1 0.338 0.338
[root@artisan ~]# jstat -gcutil 5811 2000 10000
参数解读
- S0:幸存1区当前使用比例
- S1:幸存2区当前使用比例
- E:伊甸园区使用比例
- O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
-gccause 额外输出导致上一次GC产生的原因
与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
代码语言:javascript复制[root@artisan ~]# jstat -gccause 5811
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC
0.00 0.00 25.75 2.00 97.09 93.23 0 0.000 1 0.338 0.338 Metadata GC Threshold No GC
[root@artisan ~]# jstat -gccause 5811 2000 10000
- LGCC : 上一次GC的原因
堆内存统计
代码语言:javascript复制[root@artisan ~]# jstat -gccapacity 5811
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
349504.0 349504.0 349504.0 34944.0 34944.0 279616.0 699072.0 699072.0 699072.0 699072.0 0.0 1069056.0 21248.0 0.0 1048576.0 2560.0 0 1
[root@artisan ~]# jstat -gccapacity 5811 2000 10000 --> 2秒输出一次 输出1万次
参数解读
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0C:第一个幸存区大小
- S1C:第二个幸存区的大小
- EC:伊甸园区的大小
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:当前老年代大小
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代gc次数
- FGC:老年代GC次数
新生代垃圾回收统计
代码语言:javascript复制[root@artisan ~]# jstat -gcnew 5811
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
34944.0 34944.0 0.0 0.0 15 15 0.0 279616.0 56627.5 0 0.000
[root@artisan ~]# jstat -gcnew 5811 2000 10000 --> 2秒输出一次 输出1万次
参数解读
- S0C:第一个幸存区的大小
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- TT:对象在新生代存活的次数
- MTT:对象在新生代存活的最大次数
- DSS:期望的幸存区大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间
新生代内存统计
代码语言:javascript复制[root@artisan ~]# jstat -gcnewcapacity 5811
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
349504.0 349504.0 349504.0 34944.0 34944.0 34944.0 34944.0 279616.0 279616.0 0 1
[root@artisan ~]# jstat -gcnewcapacity 5811 2000 10000 --> 2秒输出一次 输出1万次
参数解读
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0CMX:最大幸存1区大小
- S0C:当前幸存1区大小
- S1CMX:最大幸存2区大小
- S1C:当前幸存2区大小
- ECMX:最大伊甸园区大小
- EC:当前伊甸园区大小
- YGC:年轻代垃圾回收次数
- FGC:老年代回收次数
老年代垃圾回收统计
代码语言:javascript复制[root@artisan ~]# jstat -gcold 5811
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
21248.0 20630.5 2560.0 2386.8 699072.0 13979.2 0 1 0.338 0.338
[root@artisan ~]# jstat -gcold 5811 2000 10000 --> 2秒输出一次 输出1万次
参数解读
- MC:方法区大小
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- OC:老年代大小
- OU:老年代使用大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
老年代内存统计
代码语言:javascript复制[root@artisan ~]# jstat -gcoldcapacity 5811
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
699072.0 699072.0 699072.0 699072.0 0 1 0.338 0.338
[root@artisan ~]# jstat -gcoldcapacity 5811 2000 10000
参数解读
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:老年代大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
元数据空间统计
代码语言:javascript复制[root@artisan ~]# jstat -gcmetacapacity 5811
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
0.0 1069056.0 21248.0 0.0 1048576.0 2560.0 0 1 0.338 0.338
[root@artisan ~]# jstat -gcmetacapacity 5811 2000 10000
参数解读
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
运行情况预估
jstat 会用了哈
主要是利用jstat -gc pid 算出系统的关键参数 ,结合我们之前梳理的理论知识(这个没有,那基本上是不太可行了) ,先设置一些初始值,然后根据压测情况合理调整。
主要观察一下几点
【年轻代对象增长的速率】
可以执行命令 jstat -gc pid 2000 100(每隔2秒执行1次命令,共执行100次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象.
如果系统负载不高,可以把频率2秒调大一些观察整体情况。
要考虑: 系统高峰期和低谷期间的压力,肯定是不一样的,总和考虑。
【Young GC的触发频率和每次耗时】
知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。
【每次Young GC后有多少对象存活和进入老年代】
这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。
【Full GC的触发频率和每次耗时】
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。