---
title: JVM GC 算法与 Stop The World
type: concept
domain: java-concurrency
tags: [jvm, gc, algorithm, mark-sweep, copy, mark-compact, stw, safepoint, write-barrier]
status: learning
created: 2026-09-22
last_reviewed: 2026-09-22
method: feynman
related_questions:
  - "JVM有哪些垃圾回收算法？"
  - "什么是Stop The World？"
  - "Java的堆是如何分代的？为什么分代？"
  - "JVM 中一次完整的 GC 流程是怎样的？"
related_knowledge:
  - ./jvm-gc-concurrent.md
  - ./jvm-gc-select-tune.md
  - ./jvm-memory-model.md
anki_cards: 5
interview_rounds:
  - "2026-09-22-round-1-Q4"
  - "2026-09-22-round-1-Q5"
  - "2026-09-22-round-1-Q6"
  - "2026-09-22-round-1-Q7"
---

# JVM GC 算法与 Stop The World

> 三种经典算法：**标记-清除、复制、标记-整理**。GC 器 = 算法组合 + 执行方式。STW 的本质是"GC 与线程之间的互斥锁"。

## TL;DR（30 秒扫完）

- 三大算法：标记-清除（有碎片）/ 复制（无碎片但浪费空间）/ 标记-整理（无碎片但移动开销）
- **新生代用复制**（朝生夕死，存活少），**老年代用标记-整理**（存活多）
- 权衡三角：**吞吐 vs 停顿 vs 空间**——不可兼得
- STW = "GC 与线程的互斥锁"，用短暂停顿换状态一致性
- GC 阶段：**Initial Mark（STW）→ Concurrent Mark → Remark（STW）→ Concurrent Sweep**
- **CMS 会退化 Full GC**（Concurrent Mode Failure），G1/ZGC 尽量避免

## 关键结论

- **结论 A**：GC 器都是三大算法在不同代上的组合（ParallelGC / CMS / G1 / ZGC）
- **结论 B**：Eden:From:To = **8:1:1**，Survivor 只留 10% 逼早熟对象晋升
- **结论 C**：STW 集中在 **Initial Mark + Remark** 两个阶段，Concurrent 阶段不 STW
- **结论 D**：STW 无法完全消除，只能缩短；ZGC 通过读屏障+多映射压到 <1ms
- **结论 E**：**GC 流程分两层**——对象晋升 + 算法执行，答"完整流程"必须两层都说

## 完整讲解（费曼四步）

### STEP 1 · 概念

GC 算法核心是"标记存活 + 清理死对象"两种操作的不同组合。**标记-清除** = 只标记不整理，**复制** = 存活复制到另一块空间，**标记-整理** = 标记后压缩到一侧。三者权衡：**吞吐、停顿、空间**不可兼得。

### STEP 2 · 大白话

> **类比**：打扫房间（GC）：
> - 标记-清除：把垃圾围起来（标记），把垃圾扔出去（清理）——省事，但地上留下许多散落的空盒子（碎片）
> - 复制：把有用的东西全搬到隔壁房间（复制），把整个房间烧掉（清空）——干净，但浪费隔壁房间
> - 标记-整理：把有用的东西按顺序挤到一边（整理），把剩下的一半扔掉——最省空间，但搬东西耗时

### STEP 3 · 底层

#### 三大算法对比

| 算法 | 流程 | 优点 | 缺点 | 使用者 |
|------|------|------|------|--------|
| 标记-清除 | GC Roots 标记 → 清理未标 | 简单、无需移动 | **内存碎片** | CMS 老年代、ZGC 早期 |
| 复制 | 堆分两半，存活复制到另一半，清空旧半 | **无碎片** | **浪费一半空间** | 所有分代 GC 的新生代 |
| 标记-整理 | 标记存活 → 压缩到一侧 | 无碎片、空间利用率高 | **移动对象要处理所有引用**（写屏障） | ParallelGC、G1 Region 间、ZGC |

#### GC 器 = 算法组合 + 执行方式

| GC 器 | 新生代 | 老年代 | 停顿特征 |
|-------|-------|--------|---------|
| Serial | 复制 | 标记-整理 | 单线程，短停顿 |
| ParallelGC（JDK 8 默认） | 复制 | 并行标记-整理 | 高吞吐 |
| CMS | ParNew(复制) | 并发标记-清除 | 低延迟但有碎片（JDK 14 弃用） |
| G1（JDK 9+ 默认） | Region 复制 | Region 间标记-整理 | 可预测停顿 |
| ZGC / Shenandoah | 同堆 | 并发标记-整理+读屏障 | <10ms 停顿 |

#### 分代结构（HotSpot 默认）

```
Heap
├── Young Generation (约 1/3)
│   ├── Eden (约 80%)         # 新对象首次分配
│   ├── From Survivor (约 10%) # 上一次存活
│   └── To Survivor (约 10%)   # 本次存活目标
└── Old Generation (约 2/3)    # 长寿 + 大对象
+ Metaspace (JDK 8+ 独立于堆)
```

**晋升 4 条规则**：
1. **年龄晋升**：GC 次数达 `MaxTenuringThreshold`（默认 15，动态）
2. **动态年龄判定**：Survivor 中同龄对象总和 > 50%，该年龄及更大全部晋升
3. **大对象晋升**：超 `PretenureSizeThreshold` 直接老年代分配
4. **空间分配担保**：Survivor 装不下就搬老年代

#### GC 流程两层（重要！）

**第一层：对象晋升**
- Eden 满 → Young GC → 存活复制到 To Survivor → From/To 交换 → 反复几次晋升老年代

**第二层：算法执行（以 CMS 为例）**
1. **Initial Mark（STW）**：只标 GC Roots，快
2. **Concurrent Mark**：并发遍历，不 STW
3. **Remark（STW）**：处理引用变动，靠写屏障/Remembered Set 缩短
4. **Concurrent Sweep**：并发清理，不 STW（有碎片）

Full GC 传统：**STW 整代标记 → 标记-整理 → 清理尾部**

#### 三类 GC 的边界

| 类型 | 回收范围 | 说明 |
|------|---------|------|
| Young GC | 只新生代 | STW 短（几十 ms），频率高 |
| Mixed GC（G1） | Young + 部分老年代 Region | 按脏度排序，可预测停顿 |
| Full GC | 整代 | STW 长（秒级），最后手段 |

**触发 Full GC**：老年代满 / Metaspace 满 / `System.gc()` / Survivor 晋升失败

#### STW 本质与 Safepoint

**本质**：GC 与线程之间的**互斥锁**——用短暂停顿换引用图的一致性分析

**实现**：Safepoint（安全点）
- JVM 通知线程跑到最近的 Safepoint 才暂停
- Safepoint = 字节码特定位置：方法调用、循环回跳、异常跳转
- 长方法无 Safepoint 会拖长 STW（HotSpot 通过 `-XX:LoopSafepointPollSize` 自动插入检查）
- 排查：`jcmd <pid> VM.print_safepoints`、JFR `jdk.Safepoint`

#### STW 阶段分布

| 阶段 | STW？ | 说明 |
|------|-------|------|
| Initial Mark | ✅ STW | 只标 GC Roots，快 |
| Concurrent Mark | ❌ 并发 | GC 线程+用户线程并发 |
| Remark | ✅ STW | 处理引用变动，靠写屏障缩短 |
| Concurrent Sweep | ❌ 并发 | CMS 有，G1/ZGC 用 Concurrent Relocate |

#### Concurrent Mode Failure

并发标记期间老年代空间不足 → 退化 Full GC → 停顿从几十 ms 飙到秒级。这就是"退化回单线程"，是线上 GC 事故的常见根因。

### STEP 4 · 简化

> **一句话总结**：**新生代复制、老年代整理**；GC 流程分两层（对象晋升+算法执行）；STW 集中在 Initial Mark+Remark；无法消除只能缩短。

**记忆口诀**：
- 三大算法：清除 / 复制 / 整理
- 权衡三角：吞吐 / 停顿 / 空间
- STW 阶段：Initial Mark + Remark
- 分代动机：弱分代假说（朝生夕死）

## 常见误区

- **误区 1**："只有 Full GC 才动老年代" → 正确：**G1 Mixed GC** 也会回收部分老年代 Region
- **误区 2**："JVM 会自动把对象搬到堆外" → 正确：堆外是独立机制，需主动使用
- **误区 3**："STW 可以完全消除" → 正确：只能缩短（ZGC <1ms），Full GC 仍是秒级
- **误区 4**："15 次是 CMS 参数" → 正确：`MaxTenuringThreshold` 是 HotSpot 通用配置
- **误区 5**："G1 的 Region 内也会整理" → 正确：Region 内不整理（避免移动开销），跨 Region 才复制

## 延伸追问

1. **为什么新生代用复制不用标记-清除？**
   - 朝生夕死，存活少，复制快无碎片；标记-清除新生代虽快但碎片累积影响 Eden 分配
2. **Eden:Survivor = 8:1 是什么原理？**
   - JDK 8 默认 8:1:1，Survivor 只留 ~10% 逼早熟对象晋升；调优 `-XX:PretenureSizeThreshold` 和 `-XX:MaxTenuringThreshold`
3. **写屏障是什么？为什么并发 GC 必需？**
   - 写屏障是对象引用赋值时插入的额外代码（CardTable 记录跨代指针），让 GC 高效处理并发修改
4. **Remark 为什么需要 STW？如何缩短？**
   - 要处理并发标记期间的引用变动，必须 STW 保证一致性；写屏障让变动可追踪，Remark 只处理"脏"引用
5. **如何在生产上观察 GC 流程？**
   - `-Xlog:gc*` 完整日志、JFR `jdk.GCPhasePause` 事件、Arthas `dashboard`、关注 `Pause Remark` 和 Full GC 频率

## 速查表

```
三大算法: 标记-清除 / 复制 / 标记-整理
分代: 新生代复制 / 老年代整理
权衡三角: 吞吐 / 停顿 / 空间
分代结构: Eden 80% + From 10% + To 10% + Old 2/3
晋升 4 规则: 年龄 / 动态年龄 / 大对象 / 空间担保
GC 阶段: Initial Mark (STW) → Concurrent Mark → Remark (STW) → Sweep
三类 GC: Young / Mixed (G1) / Full
STW 阶段: Initial Mark + Remark
CMS 退化: Concurrent Mode Failure
```

## 关联题目（题库）

- ⚠️ 《JVM有哪些垃圾回收算法？》— Round 1 Q4, ⭐⭐⭐⭐（术语不准，漏 ZGC）
- ⚠️ 《Java的堆是如何分代的？为什么分代？》— Round 1 Q5, ⭐⭐⭐（漏 Mixed GC 边界、缺具体结构）
- ⚠️ 《什么是Stop The World？》— Round 1 Q6, ⭐⭐⭐（缺 Safepoint，术语混乱）
- ⚠️ 《JVM 中一次完整的 GC 流程是怎样的？》— Round 1 Q7, ⭐⭐（漏算法执行层）

## 关联知识

- [JVM GC 器选型与调优](./jvm-gc-select-tune.md)
- [JVM GC 并发机制与三色标记](./jvm-gc-concurrent.md)
- [JVM 内存模型](./jvm-memory-model.md)
- [主题地图](./_moc.md)

## Anki 候选卡片

1. **正**：三大 GC 算法？**反**：标记-清除 / 复制 / 标记-整理
2. **正**：Eden:From:To 默认比例？**反**：8:1:1
3. **正**：晋升老年代 4 条规则？**反**：年龄晋升 / 动态年龄判定 / 大对象晋升 / 空间分配担保
4. **正**：哪些 GC 阶段 STW？**反**：Initial Mark + Remark
5. **正**：CMS 退化 Full GC 的原因？**反**：并发标记期间老年代空间不足（Concurrent Mode Failure）

---

*最后更新：2026-09-22*
