:::info 使用工具来分析 JVM 的运行状态:
- 新生代对象的增长的速率;
- Young GC 的触发频率;
- Young GC 的耗时;
- 每次 Young GC 后有多少对象是存活下来的;
- 每次 Young GC 后有多少对象进入了老年代;
- 老年代对象增长的速率;
- Full GC 的触发频率;
- Full GC 的耗时 :::
1. jstat 功能
- JDK 自带的工具;
- 可以看到 JVM 内的 Eden、Survivor、老年代的内存情况;
- Young GC、Full GC 的执行次数、耗时;
:::info
使用 jstat 前需要提前获取 Java 进程的 PID,使用以下命令:jps
:::
2. jstat 命令
jstat -gc PID:查看一个 JVM 进程的内存和 GC 情况
- S0C:这是 From Survivor 区的大小
- S1C:这是 To Survivor 区的大小
- S0U:这是 From Survivor 区当前使用的内存大小
- S1U:这是 To Survivor 区当前使用的内存大小
- EC:这是 Eden 区的大小
- EU:这是 Eden 区当前使用的内存大小
- OC:这是老年代的大小
- OU:这是老年代当前使用的内存大小
- MC:这是方法区(永久代、元数据区)的大小
- MU:这是方法区(永久代、元数据区)的当前使用的内存大小
- YGC:这是系统运行迄今为止的 Young GC 次数
- YGCT:这是 Young GC 的耗时
- FGC:这是系统运行迄今为止的 Full GC 次数
- FGCT:这是 Full GC 的耗时
- GCT:这是所有 GC 的总耗时
$ jstat -gc 51808
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 0.0 8192.0 0.0 10240.0 1054.5 4864.0 3995.1 512.0 446.8 111024 33.054 0 0.000 33.054
其他的 jstat 命令:
- jstat -gccapacity PID:堆内存分析
- jstat -gcnew PID:年轻代GC分析,这里的TT和MTT可以看到对象在年轻代存活的年龄和存活的最大年龄
- jstat -gcnewcapacity PID:年轻代内存分析
- jstat -gcold PID:老年代GC分析
- jstat -gcoldcapacity PID:老年代内存分析
- jstat -gcmetacapacity PID:元数据区内存分析
3. 如何通过 jstat 获得我们想要的信息?
(1) 新生代对象增长的速率
- 要优化 JVM,需要了解的第一件事就是,分析出每秒系统会在新生代的 Eden 区分配多少空间的对象;
- “
jstat -gc PID 1000 10
“,每秒进行一次 jstat 信息统计,一共执行10次;- 观察每隔一段时间的 JVM 中的 Eden 区对象占用变化;
- 例如,1s Eden used 200MB,2s Eden used 205MB,3s Eden used 209MB,推断出大概每秒会新增5MB左右的对象;
一般需要对系统的高峰和日常两个时间段内的对象增长速率有清晰的了解;
(2) Young GC 的触发频率和平均耗时
根据新生代对象增长的速率,推测出多久发生一次 Young GC;
- 例如,Eden 容量800MB,高峰期每秒新增5MB的对象,大概高峰期就是3分钟触发一次 Young GC;日常期每秒新增0.5MB对象,那么日常期大概半小时才触发一次;
可以间隔24小时,执行两次 jstat -gc 获取24小时内发生了多少次 Young GC,及这些 Young GC 的总耗时;
每次 Young GC 过后有多少对象会存活下来,这个没法直接看出来,但是有办法可以大致推测出来。
- 已经推算出了高峰期多久发生一次 Young GC,比如 3分钟左右会有一次 Young GC;
- 执行 “
jstat -gc PID 180000 10
“,每3分钟执行一次统计,连续执行10次; - 观察一下,每隔三分钟之后发生一次的 Young GC,此时 Eden、Survivor、老年代的对象变化;
- 重点观察老年代的对象增长速率,例如,每3分钟会有50M对象进入老年代,老年代对象的增长速率,就是3分钟增长50M;
- 每次 Young GC 后有多少存活对象,就是观察 Survivor 和进入老年代的对象,都是存活对象;
通常来说,老年代的对象增长是缓慢的,通常每次 Young GC 后新增几百KB、几MB的对象是正常的,如果发现每次 Young GC 后,老年代新增几十MB,那就不正常,很有可能是一次 Young GC 后存活对象太多,导致放入 Survivor 后触发了动态对象年龄判定队则进入老年代,或 Suvivor 放不下,大部分存活对象进入老年代;
(4) Full GC 的触发时机和平均耗时
根据老年代对象的增长速率,那么 Full GC 的触发时机就可以推测了;
- 例如,老年嗲 容量800MB,每3分钟新增50MB的对象,大概每小时就会触发一次 Full GC;
- jstat 可以统计出系统运行到目前位置的 Full GC 总次数和总耗时,例如,以共执行了 10次 Full GC,共耗时 30s,每次 Full GC 的平均时间大概 3s 左右;