主题
界面
主题
同一程序(100 轮 x 1MB 短命对象)、128M 堆、三种收集器的一手对比(-Xlog:gc 采集):
SerialGC :Pause 5 次,例 18M->2M 0.591ms ← 单次停顿最短 ✓✓
ParallelGC :Pause 4 次,例 33M->1M 1.346ms
G1GC :Pause 3 次,例 38M->1M 1.590ms ← 次数最少 ✓
全部 0 次 Full GC ✓★★ 一个反直觉的实测结论:小堆(128M)下 G1 并不占优,Serial 单次停顿最短 ✓✓ —— G1 官方建议用于 6G+ 堆 ✓ ——收集器选型必须结合堆大小 ✓✓✓
★ GC 停顿 = 全世界暂停 ✗✗(除 ZGC 亚毫秒外)—— P99 毛刺的第一嫌疑人就是 GC ✗✓(呼应 T2-014)
| 算法 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 标记-清除 | 标记存活,清除垃圾 | 简单 | 内存碎片 ✗✗ |
| 标记-复制 | 存活对象复制到另一半 | 无碎片、快 ✓✓ | 浪费一半空间 ✗ |
| 标记-整理 | 标记后向一端移动 | 无碎片、不浪费 | 移动对象成本高 ⚠️ |
★ 分代的由来 ✓✓✓:绝大多数对象朝生夕死(《JVM 内存结构与对象创建》 实测:100MB 短命对象一次 Young GC 就清完 ✓)—— 新生代用复制(存活少 → 复制成本低 ✓✓);老年代用标记-整理/清除 ✓✓✓
| 收集器 | 算法 | 线程 | 适用 |
|---|---|---|---|
| Serial | 复制 + 整理 | 单线程 | 小堆/单核/客户端 ✓✓ |
| Parallel | 复制 + 整理 | 多线程(吞吐优先) | 批处理/计算 ✓ |
| G1(默认) | Region 复制+整理 | 并发+并行 | 大堆(6G+)低延迟 ✓✓ |
| ZGC | 着色指针并发 | 并发 | 超大堆、亚毫秒停顿 ✓✓✓ |
| Shenandoah | 并发整理 | 并发 | 同 ZGC ✓ |
★ JDK 21 的默认就是 G1(本节实测 "Using G1" ✓);CMS 已移除 ✗
堆 < 1G 或单核 → Serial(实测单次停顿 0.59ms 最短)✓✓
吞吐优先(批处理) → Parallel(吞吐 = 总停顿最短)✓
大堆 + 低延迟(在线服务)→ G1(6G+)✓✓
超大堆 + 极致延迟 → ZGC(<1ms,牺牲吞吐 10~15%)⚠️✓★ 本节实测的选型印证 ✓✓✓:
128M 小堆:Serial 每次回收 18M->2M 用 0.59ms —— G1 的 Region 多、根扫描成本在小堆上不划算 ✓✓ —— 「没有最好的收集器,只有匹配堆大小与延迟目标的收集器」 ✓✓✓
GC(3) Pause Young (Allocation Failure) 33M->1M(83M) 1.346ms
↑原因 ↑回收前→回收后(堆容量) ↑停顿★ 健康判据 ✓✓:
| 信号 | 判断 |
|---|---|
| Young 频繁但每次 <10ms | 正常 ✓ |
| 老年代增长快 → 频繁 Mixed/Full ✗✗ | 泄漏或缓存过大 ⚠️ |
| Full GC 出现 ✗✗✗ | 立即排查(Meta/Humongous/碎片)✓✓ |
| Pause 随时间变长 ✗ | 碎片化 ✗ |
foreach ($g in @("SerialGC","ParallelGC","G1GC")) {
java "-XX:+Use$g" "-Xlog:gc" "-Xmx128m" "-Xms64m" -cp java-deep/target/classes cn.kb.learn.deep.jvm.GcLesson
}ZGC 自身需要额外内存与并发线程 —— 128M 堆上得不偿失 ✗✗代码:labs/java-deep/src/main/java/cn/kb/learn/deep/jvm/GcLesson.java
实测输出(关键部分):
SerialGC : Pause=5 例 18M->2M(61M) 0.591ms
ParallelGC : Pause=4 例 33M->1M(83M) 1.346ms
G1GC : Pause=3 例 38M->1M(66M) 1.590ms
全部 Full=0;三方案剩余库存/结果一致 ✓★ 三个数字读法 ✓✓:33M->1M = 回收前活 33M、回收后活 1M(32M 是垃圾被回收 ✓)、堆容量 83M ✓
| # | 错误 | 说明 |
|---|---|---|
| 1 | 小堆用 G1 ⚠️ | 实测不占优 ✓✓ |
| 2 | 误把 "Allocation Failure" 当错误 ✗✗ | 这是 Young GC 的正常原因 ✓✓ |
| 3 | 忽略 Full GC ✗✗✗ | 它是最危险的信号 ✓✓ |
| 4 | ZGC 当默认 ✗ | 吞吐损失 10~15% ⚠️ |
| 5 | 只调收集器不查泄漏 ✗✗ | 泄漏时换任何收集器都白搭 ✓✓ |
验收方式:给一份 GC 日志能判断收集器、代行为与健康状态。
扩展任务:写一份「本服务 GC 基线」(收集器/堆参数/正常 GC 频率与停顿),供告警阈值参考 ✓✓