
指令解释
ps -ef | grep java 查找自己的java进程
这个指令是 jstat -gcutil 34817 1000。
它是 Java 开发中非常常用的一个监控命令,用于实时查看 Java 进程的垃圾回收(GC)情况。
下面是详细的拆解说明:
1. 指令拆解
jstat: 是 JDK 自带的一个工具(JVM Statistics Monitoring Tool),专门用来监视 JVM 的各种运行时信息,特别是 GC 和类加载情况。-gcutil: 这是参数选项,意思是 Garbage Collection Utilization(垃圾回收利用率)。它会让输出结果以**百分比(%)**的形式展示各个内存区域的使用情况,比看具体的 KB/MB 数值更直观。34817: 这是 进程 ID (PID)。表示你正在监控 PID 为 34817 的那个 Java 程序。1000: 这是 时间间隔(单位是毫秒)。表示每隔 1000 毫秒(即 1 秒)刷新并打印一次数据。如果不加这个参数,它只会打印一次就退出了。
2. 输出结果(表头)含义
图片下方那一堆数字对应的列头含义如下:
- S0 / S1: Survivor 0区 / Survivor 1区 的使用率(百分比)。
- E: Eden 区(新生代的主要部分)的使用率。
- O: Old 区(老年代)的使用率。
- M: Metaspace(元空间,JDK 8+ 取代了永久代)的使用率。
- CCS: Compressed Class Space(压缩类空间)的使用率。
- YGC: Young Generation GC 次数(年轻代垃圾回收发生的总次数)。
- YGCT: Young Generation GC 总耗时(秒)。
- FGC: Full GC 次数(全堆垃圾回收发生的总次数)。
- FGCT: Full GC 总耗时(秒)。
- GCT: 垃圾回收总耗时(秒)。
3. 从你的截图中能看出什么?
根据截图中的数据,可以简单分析一下这个 Java 程序的状态:
- 系统很稳定,没有频繁 GC :看
YGC(115) 和FGC(4) 这两列,数字一直没变,说明在这十几秒的监控期间,没有发生新的垃圾回收。 - 元空间(Metaspace)占用极高 :看
M这一列,数值高达 92.33% 。- 风险提示 :这通常意味着加载的类非常多,或者存在类加载泄露的风险。如果这个值达到 100%,可能会抛出
java.lang.OutOfMemoryError: Metaspace错误。建议检查一下是否引入了过多的 jar 包,或者是否有动态代理类生成过多的情况。
- 风险提示 :这通常意味着加载的类非常多,或者存在类加载泄露的风险。如果这个值达到 100%,可能会抛出
- Eden 区在缓慢增长 :看
E这一列,从 7.57 慢慢涨到了 13.81,说明程序正在正常创建新对象,但还没满到触发 Young GC 的程度。
截图2

JStat GC 数据分析
📊 关键指标解读
1. ⚠️ 严重问题:S1 持续 100%
- S0: 一直 0.00%
- S1: 一直 100.00%
- 问题:Survivor 区分配不均,S1 已满但对象无法晋升或移动
2. ⚠️ Eden 区持续增长
34.85% → 37.88% → 39.39% → ... → 62.12%
- 10秒内增长了 27.27%
- 说明程序在不断创建新对象
- 但 YGC 次数没有增加(保持23次),这很不正常!
3. ⚠️ Metaspace 告急
- M: 97.06% (几乎满了!)
- CCS: 93.23% (压缩类空间也很高)
- 风险 :可能很快会报
java.lang.OutOfMemoryError: Metaspace
4. 老年代稳定
- O: 20.90% (正常,没有增长)
5. GC 活动停滞
- YGC: 23次(无新增)
- FGC: 0次(没有Full GC)
- CGC: 16次(Concurrent GC,G1垃圾回收器的特征)
🚨 问题诊断
可能的原因:
-
Survivor 区太小
- S1 已经 100%,对象无法在 Survivor 之间复制
- 可能导致对象过早晋升到老年代或直接进入 Humongous 区
-
Metaspace 泄露风险
- 97% 的使用率非常危险
- 可能原因:
- 动态生成类过多(CGLIB、ASM等)
- 类加载器泄露
- Metaspace 配置太小
-
GC 似乎被阻塞或暂停
- Eden 在增长但没有触发 YGC
- 可能 G1 正在执行 Concurrent Mark 阶段
💡 建议
立即检查:
bash
# 1. 查看 Metaspace 详细情况
jcmd 12646 VM.metaspace
# 2. 查看堆详情
jmap -heap 12646
# 3. 如果 Metaspace 持续上涨,考虑 dump
jmap -dump:format=b,file=heap.hprof 12646
JVM 参数优化建议:
bash
# 增加 Metaspace
-XX:MaxMetaspaceSize=512m
# 调整 Survivor 区
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=15
# 如果是 G1,调整 Region
-XX:G1NewSizePercent=20
最紧急的是 Metaspace 问题,建议尽快排查! ⚠️
Java 堆内存详解
让我用通俗易懂的方式给你讲解 Java 的内存分区!
📦 整体结构
Java 堆内存 (Heap)
├── 新生代 (Young Generation) ── 存放新创建的对象
│ ├── Eden 区 ── 对象出生地
│ ├── Survivor 0 区 (S0) ── 幸存者区1
│ └── Survivor 1 区 (S1) ── 幸存者区2
│
└── 老年代 (Old Generation) ── 存放长期存活的对象
🎯 各个区域的作用
1️⃣ Eden 区(伊甸园)
- 作用 :所有新创建的对象都在这里出生
- 特点:空间最大(通常占新生代的 80%)
- 比喻:就像"新生儿病房",所有新对象都先放这里
2️⃣ Survivor 区(幸存者区)- S0 和 S1
- 作用:存放经过垃圾回收后还"活着"的对象
- 特点 :
- 有两个(S0 和 S1),同一时间只有一个在使用
- 空间较小(各占新生代的 10%)
- 永远有一个是空的
- 比喻:就像"幼儿园",对象要在这里"成长"一段时间
3️⃣ 老年代(Old Generation)
- 作用 :存放长期存活的对象
- 特点:空间最大
- 比喻:就像"成人社会",对象在这里稳定生活
🔄 对象的生命周期(重点!)
让我用一个例子说明对象是如何在这些区域间移动的:
第1步:创建对象
┌─────────────────────────────────┐
│ Eden: [新对象A][新对象B]... │ ← 所有新对象都在这里
│ S0: [空] │
│ S1: [空] │
└─────────────────────────────────┘
第2步:Eden 满了,触发 Minor GC
┌─────────────────────────────────┐
│ Eden: [新对象C][新对象D]... │ ← 新对象继续在这里
│ S0: [对象A][对象B] │ ← 还活着的对象移到这里
│ S1: [空] │ ← 始终保持一个空
└─────────────────────────────────┘
↑ 死亡的对象被清理掉
第3步:Eden 又满了,再次 GC
┌─────────────────────────────────┐
│ Eden: [新对象E][新对象F]... │
│ S0: [对象A][对象B] │ ← 上次活下来的
│ S1: [对象A][对象B][对象C] │ ← 这次活下来的移到这里
└─────────────────────────────────┘
↑ S0 和 Eden 中活着的对象都复制到 S1
↑ 然后清空 S0 和 Eden
第4步:继续 GC,S1 满了
┌─────────────────────────────────┐
│ Eden: [新对象G]... │
│ S0: [对象A][对象B][对象C] │ ← 又移回 S0
│ S1: [空] │ ← 清空
└─────────────────────────────────┘
↑ 对象在 S0 和 S1 之间来回复制
第5步:对象年龄够了,晋升老年代
┌─────────────────────────────────┐
│ Eden: [新对象H]... │
│ S0: [对象D][对象E] │
│ S1: [空] │
│ Old: [对象A][对象B][对象C] │ ← 长期存活的对象
└─────────────────────────────────┘
↑ 经过多次 GC 还活着的对象进入老年代
🎮 核心机制:复制算法
为什么要有 S0 和 S1 两个 Survivor 区?
这是为了使用复制算法(Copying Algorithm):
GC 前:
┌──────────┬──────────┬──────────┐
│ Eden │ S0 │ S1 │
│ [活][死] │ [活][死] │ 空 │
└──────────┴──────────┴──────────┘
GC 时:把活着的对象复制到另一个 Survivor 区
┌────────────────────┬──────────
│ Eden │ S0 │ S1 │
│ 清空 │ 清空 │ [活][活] │ ← 复制过来
└──────────┴──────────┴──────────┘
GC 后:一次性清空 Eden 和 S0
好处:
- ✅ 没有内存碎片
- ✅ GC 效率高(只复制活着的对象)
- ✅ 实现简单
📊 你的 jstat 数据分析
回到你之前的截图:
S0 S1 E O M
0.00 100.00 62.12 20.90 97.06
问题解读:
- S0 = 0%:S0 是空的
- S1 = 100%:S1 完全满了!
- E = 62.12%:Eden 还在增长
这意味着:
- ⚠️ S1 已经满了,但对象还在 Eden 增长
- ⚠️ 下次 GC 时,S1 中的对象可能无处可去
- ⚠️ 可能导致对象直接晋升到老年代,或者触发 Full GC
🎯 对象晋升规则
对象在什么情况下会进入老年代?
- 年龄达到阈值(默认 15 次 GC)
- Survivor 区放不下(你的情况!)
- 大对象直接进入(比如大数组)
💡 总结
| 区域 | 作用 | 大小比例 | 特点 |
|---|---|---|---|
| Eden | 新对象出生地 | 80% | 对象最多,GC 最频繁 |
| S0/S1 | 存放短期存活对象 | 各 10% | 来回复制,总有一个是空的 |
| Old | 存放长期存活对象 | 最大 | GC 较少,但一旦 GC 就很慢 |
你的问题:S1 满了但对象还在增加,需要调整 Survivor 区大小或检查是否有内存泄漏!
有问题随时问我! 😊
https://chat.qwen.ai/s/5666ab1b-2bd1-402b-a451-25647e803a96?fev=0.2.46