目录
- Jstat 垃圾回收统计实用指南
- 一、基础使用说明
- 1. 核心语法格式
- 2. 快速示例
- 3. 单位说明
- 二、常用命令详解
- 1. `-gc`:显示 GC 次数、时间及堆内存各区域大小/使用量
- 2. `-gcutil`:以百分比形式统计 GC 核心信息
- 3. `-gccapacity`:堆内存与方法区容量边界统计
- 4. `-gcnew`:新生代垃圾回收详细统计
- 5. `-gcnewcapacity`:新生代内存容量详细统计
- 6. `-gcold`:老年代垃圾回收详细统计
- 7. `-gcoldcapacity`:老年代内存容量详细统计
- 8. `-gccause`:最近一次 GC 统计及回收原因(实用高频)
- 三、常见 GC 回收原因说明
- 四、实用排查思路与总结
Jstat 垃圾回收统计实用指南
jstat 是 JDK 自带的轻量级性能监控工具,专门用于收集 JVM 内存与垃圾回收(GC)相关统计信息。它无需提前部署,仅通过进程 ID(PID)即可快速获取实时数据,是排查 JVM 内存泄漏、GC 频繁等问题的必备工具之一。本文将详细讲解 jstat 常用的 GC 相关命令、输出字段含义及实用场景。
一、基础使用说明
1. 核心语法格式
jstat[选项]<进程PID>[采样间隔时间][采样次数]- 选项:指定要获取的统计信息类型(如下文的
-gc、-gcutil等)。 - 进程PID:运行中的 Java 应用进程 ID(可通过
jps、ps -ef | grep java命令获取)。 - 采样间隔时间:可选参数,指定每次统计的时间间隔,支持单位
ms(毫秒,默认)、s(秒)、m(分钟),例如3s表示每 3 秒统计一次。 - 采样次数:可选参数,指定统计的总次数,省略则表示无限次采样,直到手动终止(
Ctrl + C)。
2. 快速示例
# 仅统计一次 PID 为 80 的 Java 进程 GC 信息jstat -gc80# 每 3 秒统计一次 PID 为 80 的 Java 进程 GC 信息,无限次采样jstat -gcutil803s# 每 1 秒统计一次 PID 为 80 的 Java 进程 GC 原因,共统计 10 次jstat -gccause801s103. 单位说明
本文中未特殊标注的内存单位均为字节(Byte),如需转换为更易读的单位:
- 字节 → 兆字节(MB):除以
1024 * 1024 - 字节 → 吉字节(GB):除以
1024 * 1024 * 1024 - 时间单位均为秒(s),部分字段会保留小数位(精确到毫秒级别)。
二、常用命令详解
1.-gc:显示 GC 次数、时间及堆内存各区域大小/使用量
命令格式:
jstat -gc<PID>[采样间隔][采样次数]示例:jstat -gc 80 3s(每 3 秒统计一次,无限次采样;不写间隔仅统计一次)
输出字段含义(按列顺序):
| 字段 | 含义 |
|---|---|
| S0C | 年轻代中第一个幸存区(Survivor 0)的总容量(字节) |
| S1C | 年轻代中第二个幸存区(Survivor 1)的总容量(字节) |
| S0U | 年轻代中第一个幸存区的已使用容量(字节) |
| S1U | 年轻代中第二个幸存区的已使用容量(字节) |
| EC | 年轻代中伊甸园区(Eden)的总容量(字节) |
| EU | 年轻代中伊甸园区的已使用容量(字节) |
| OC | 老年代(Old Generation)的总容量(字节) |
| OU | 老年代的已使用容量(字节) |
| MC | 方法区(元数据区,Metaspace)的总容量(字节) |
| MU | 方法区的已使用容量(字节) |
| CCSC | 压缩类空间(Compressed Class Space)的总容量(字节) |
| CCSU | 压缩类空间的已使用容量(字节) |
| YGC | 应用启动至采样时,年轻代 GC(Minor GC)的总次数 |
| YGCT | 应用启动至采样时,年轻代 GC 消耗的总时间(秒) |
| FGC | 应用启动至采样时,老年代 GC(Full GC,全量回收)的总次数 |
| FGCT | 应用启动至采样时,老年代 GC 消耗的总时间(秒) |
| GCT | 应用启动至采样时,所有 GC(Minor GC + Full GC)消耗的总时间(秒) |
适用场景:需要精准了解堆内存各区域(伊甸园区、幸存区、老年代)的容量与实际使用量,判断内存分配是否合理。
2.-gcutil:以百分比形式统计 GC 核心信息
命令格式:
jstat -gcutil<PID>[采样间隔][采样次数]示例:jstat -gcutil 80 3s(每 3 秒以百分比形式总结 GC 信息)
输出字段含义:
| 字段 | 含义 |
|---|---|
| S0 | 第一个幸存区已使用容量占其总容量的百分比 |
| S1 | 第二个幸存区已使用容量占其总容量的百分比 |
| E | 伊甸园区已使用容量占其总容量的百分比 |
| O | 老年代已使用容量占其总容量的百分比 |
| M | 方法区(元数据区)已使用容量占其总容量的百分比 |
| CCS | 压缩类空间已使用容量占其总容量的百分比 |
| YGC | 应用启动至采样时,年轻代 GC 总次数 |
| YGCT | 应用启动至采样时,年轻代 GC 总耗时(秒) |
| FGC | 应用启动至采样时,老年代 Full GC 总次数 |
| FGCT | 应用启动至采样时,老年代 Full GC 总耗时(秒) |
| GCT | 应用启动至采样时,所有 GC 总耗时(秒) |
适用场景:快速监控 GC 整体状态,重点关注各内存区域的使用率变化,判断是否存在内存溢出风险(如老年代使用率持续接近 100%)。
3.-gccapacity:堆内存与方法区容量边界统计
命令格式:
jstat -gccapacity<PID>输出字段含义:
| 字段 | 含义 |
|---|---|
| NGCMN | 新生代(年轻代 + 幸存区)的最小容量(字节,JVM 启动时的初始最小内存) |
| NGCMX | 新生代的最大容量(字节,新生代可扩展的上限) |
| NGC | 当前新生代的实际容量(字节) |
| S0C | 第一个幸存区的总容量(字节) |
| S1C | 第二个幸存区的总容量(字节) |
| EC | 伊甸园区的总容量(字节) |
| OGCMN | 老年代的最小容量(字节) |
| OGCMX | 老年代的最大容量(字节) |
| OGC | 当前老年代的实际容量(字节) |
| OC | 当前老年代的总容量(字节,与 OGC 一致) |
| MCMN | 方法区的最小容量(字节) |
| MCMX | 方法区的最大容量(字节) |
| MC | 当前方法区的实际容量(字节) |
| CCSMN | 压缩类空间的最小容量(字节) |
| CCSMX | 压缩类空间的最大容量(字节) |
| CCSC | 当前压缩类空间的实际容量(字节) |
| YGC | 应用启动至采样时,年轻代 GC 总次数 |
| FGC | 应用启动至采样时,老年代 Full GC 总次数 |
适用场景:查看 JVM 堆内存各区域的容量上限、下限及当前配置,验证内存参数(如-Xms、-Xmx、-XX:MetaspaceSize等)是否生效。
4.-gcnew:新生代垃圾回收详细统计
命令格式:
jstat -gcnew<PID>输出字段含义:
| 字段 | 含义 |
|---|---|
| S0C | 第一个幸存区的总容量(字节) |
| S1C | 第二个幸存区的总容量(字节) |
| S0U | 第一个幸存区的已使用容量(字节) |
| S1U | 第二个幸存区的已使用容量(字节) |
| TT | 对象在新生代中存活的阈值次数(即对象在幸存区中经过 Minor GC 回收后仍存活的最大次数,超过则进入老年代) |
| MTT | 对象在新生代中存活的最大阈值次数 |
| DSS | 期望的幸存区大小(字节,JVM 根据当前内存使用情况计算的最优幸存区容量) |
| EC | 伊甸园区的总容量(字节) |
| EU | 伊甸园区的已使用容量(字节) |
| YGC | 应用启动至采样时,年轻代 GC 总次数 |
| YGCT | 应用启动至采样时,年轻代 GC 总耗时(秒) |
适用场景:深入分析新生代的 GC 行为,了解对象在新生代的存活规律,排查新生代内存分配不合理导致的频繁 Minor GC 问题。
5.-gcnewcapacity:新生代内存容量详细统计
命令格式:
jstat -gcnewcapacity<PID>输出字段含义:
| 字段 | 含义 |
|---|---|
| NGCMN | 新生代的最小容量(字节) |
| NGCMX | 新生代的最大容量(字节) |
| NGC | 当前新生代的实际容量(字节) |
| S0CMX | 第一个幸存区的最大容量(字节) |
| S0C | 当前第一个幸存区的实际容量(字节) |
| S1CMX | 第二个幸存区的最大容量(字节) |
| S1C | 当前第二个幸存区的实际容量(字节) |
| ECMX | 伊甸园区的最大容量(字节) |
| EC | 当前伊甸园区的实际容量(字节) |
| YGC | 应用启动至采样时,年轻代 GC 总次数 |
| FGC | 应用启动至采样时,老年代 Full GC 总次数 |
适用场景:专注于新生代的容量配置分析,查看幸存区、伊甸园区的最大可扩展容量,优化新生代内存分配比例(如-XX:SurvivorRatio配置)。
6.-gcold:老年代垃圾回收详细统计
命令格式:
jstat -gcold<PID>输出字段含义:
| 字段 | 含义 |
|---|---|
| MC | 方法区的总容量(字节) |
| MU | 方法区的已使用容量(字节) |
| CCSC | 压缩类空间的总容量(字节) |
| CCSU | 压缩类空间的已使用容量(字节) |
| OC | 老年代的总容量(字节) |
| OU | 老年代的已使用容量(字节) |
| YGC | 应用启动至采样时,年轻代 GC 总次数 |
| FGC | 应用启动至采样时,老年代 Full GC 总次数 |
| FGCT | 应用启动至采样时,老年代 Full GC 总耗时(秒) |
| GCT | 应用启动至采样时,所有 GC 总耗时(秒) |
适用场景:分析老年代的内存使用与 Full GC 情况,排查老年代对象堆积、Full GC 耗时过长等问题。
7.-gcoldcapacity:老年代内存容量详细统计
命令格式:
jstat -gcoldcapacity<PID>输出字段含义:
| 字段 | 含义 |
|---|---|
| OGCMN | 老年代的最小容量(字节) |
| OGCMX | 老年代的最大容量(字节) |
| OGC | 当前老年代的实际容量(字节) |
| OC | 老年代的总容量(字节,与 OGC 一致) |
| YGC | 应用启动至采样时,年轻代 GC 总次数 |
| FGC | 应用启动至采样时,老年代 Full GC 总次数 |
| FGCT | 应用启动至采样时,老年代 Full GC 总耗时(秒) |
| GCT | 应用启动至采样时,所有 GC 总耗时(秒) |
适用场景:查看老年代的容量边界与当前配置,验证老年代内存参数(如-Xmn、-XX:OldSize等)是否合理,判断老年代是否有足够的扩展空间。
8.-gccause:最近一次 GC 统计及回收原因(实用高频)
命令格式:
jstat -gccause<PID>[采样间隔][采样次数]输出字段含义(在前-gcutil字段基础上增加 2 个核心字段):
| 字段 | 含义 |
|---|---|
| S0/S1/E/O/M/CCS | 同-gcutil,对应各内存区域的使用百分比 |
| YGC/YGCT/FGC/FGCT/GCT | 同-gcutil,对应各 GC 次数与耗时 |
| LGCC | 最近一次垃圾回收的原因(Last GC Cause) |
| GCC | 当前正在进行的垃圾回收原因(Current GC Cause,若无正在进行的 GC,则显示 “No GC”) |
适用场景:快速定位最近一次 GC 的触发原因,是排查突发 GC 问题的首选命令,无需分析大量日志即可获取关键信息。
三、常见 GC 回收原因说明
GC 回收原因直接反映了 JVM 内存运行的状态,以下是最常见及核心的回收原因,重点掌握:
- Allocation Failure(最常见)
- 含义:新生代(伊甸园区)中没有足够的空间存储新创建的对象,触发 Minor GC(年轻代回收)。
- 说明:这是应用运行过程中的正常 GC 原因,但若频繁触发(如每秒数次),则需警惕新生代内存配置过小、对象创建速度过快或对象无法快速回收等问题。
- Metadata GC Threshold
- 含义:方法区(元数据区)的使用量达到了阈值,触发 GC 回收无用的类元数据信息。
- 说明:通常与大量动态生成类(如反射、动态代理、框架扫描)相关,若频繁触发,可适当调大方法区容量(
-XX:MetaspaceSize、-XX:MaxMetaspaceSize)。
- Concurrent Mode Failure
- 含义:CMS 垃圾回收器在并发标记或并发清理阶段,老年代内存不足,无法容纳新生代晋升的对象,导致并发回收失败,触发一次阻塞式的 Full GC。
- 说明:属于严重问题,会导致应用停顿时间过长,需优化 CMS 相关参数或更换垃圾回收器。
- GC Overhead Limit Exceeded
- 含义:JVM 在短时间内花费了大量时间进行 GC,但回收的内存却极少(默认超过 98% 的时间用于 GC,且回收的内存不足 2%),触发 OOM 错误。
- 说明:通常是内存泄漏的信号,需排查是否有大对象无法被回收(如静态集合持有大量对象引用)。
- No GC
- 含义:当前没有正在进行的垃圾回收,且最近一次 GC 已完成。
- 说明:应用内存状态稳定,无明显内存压力。
四、实用排查思路与总结
- 快速监控 GC 整体状态:优先使用
jstat -gcutil <PID> 1s,观察 O(老年代)使用率是否持续上升、FGC(Full GC)是否频繁,若 FGC 次数过多且耗时过长,需进一步分析。 - 定位 GC 触发原因:使用
jstat -gccause <PID>,通过 LGCC 字段查看最近一次 GC 原因,若为Allocation Failure频繁触发,优先优化新生代内存;若为Metadata GC Threshold,优化方法区配置。 - 验证内存参数配置:使用
jstat -gccapacity <PID>,查看 NGCMX、OGCMX 等字段,确认内存参数是否符合预期,避免因参数配置错误导致的内存问题。 - 深入分析新生代/老年代:若快速监控无法定位问题,可使用
-gcnew、-gcold命令,深入了解对象存活规律与内存分配细节,为 JVM 内存参数优化提供数据支撑。
jstat 工具的核心优势是轻量、高效、实时,无需侵入应用即可获取关键 GC 数据,是 JVM 性能监控的入门与核心工具。掌握本文中的常用命令与字段含义,能够快速应对大部分 GC 相关的性能问题,为后续深入学习 JVM 调优打下坚实基础。