diff --git a/docs/jvm/garbage-collection.md b/docs/jvm/garbage-collection.md
new file mode 100644
index 0000000..fff92cc
--- /dev/null
+++ b/docs/jvm/garbage-collection.md
@@ -0,0 +1,158 @@
+# 垃圾回收基础
+
+!!! note "垃圾回收是编程语言运行时自动管理内存的机制,核心思想:找出程序不再使用的内存并释放。"
+
+---
+
+## 什么是垃圾回收
+
+垃圾回收(Garbage Collection,简称 GC)是编程语言运行时(Runtime)自动管理内存的一种机制。基本判断逻辑:
+
+- **引用计数** — 每个对象记录有多少东西引用它,归零就回收。Python 用这种。简单高效,但解决不了循环引用。
+- **可达性分析** — 从根对象出发,遍历能走到的所有对象,走不到的就是垃圾。Java、Go、JavaScript 都用这个。
+
+## 没有 GC 的语言
+
+没有垃圾回收的语言,内存管理靠程序员手动完成:
+
+### 手动管理
+
+| 语言 | 方式 |
+|------|------|
+| **C** | `malloc` / `free` |
+| **C++** | `new` / `delete`,以及 RAII(作用域结束自动析构) |
+| **Assembly** | 直接操作内存,没有抽象层 |
+| **Fortran** | 传统上手动管理 |
+
+### 所有权 / 借用检查(零成本自动回收,但不是 GC)
+
+| 语言 | 机制 |
+|------|------|
+| **Rust** | 所有权 + 值离开作用域自动释放,编译期检查,无运行时开销 |
+| **Zig** | 手动 alloc/free,但提供了 allocator 抽象 |
+
+### 其他方式
+
+- **D** — 可选 GC,也支持手动管理
+- **Swift** — ARC(引用计数),严格来说不算传统 GC
+- **Objective-C / Swift** — ARC 机制
+
+### 对比理解
+
+```
+GC 语言(Java、Go、Python)
+ → 运行时有个"管家"定期打扫
+ → 方便,但有 STW 延迟和内存开销
+
+手动管理(C、C++)
+ → 自己打扫,扫不干净(泄漏)或扫过头(野指针)都可能出问题
+
+所有权系统(Rust)
+ → 编译期就帮你规划好谁负责清理
+ → 零运行时开销,但学习曲线陡
+```
+
+## GC 的代价
+
+垃圾回收并非免费午餐,主要代价体现在:
+
+### 1. Stop-The-World(STW)暂停
+
+GC 在某些阶段必须暂停程序执行,所有业务线程停住,等 GC 干完活。
+
+- 短暂停(几毫秒)对普通应用无所谓
+- 但在高频交易、游戏渲染、实时系统里,几毫秒的抖动就是灾难
+- Java 的 ZGC、Go 的并发 GC 都在拼命缩短这个暂停时间
+
+### 2. 额外内存开销
+
+GC 需要额外空间来做标记和跟踪:
+
+- **引用计数**:每个对象多一个计数器
+- **三色标记**:需要额外的标记位图或栈
+- **分代收集**:需要维护多个内存区域和存活/死亡对象的拷贝
+- **写屏障(Write Barrier)**:每次对象引用变更都要额外记录
+
+实际经验:**开销大约在 5%~20% 的额外内存**。
+
+### 3. CPU 时间消耗
+
+GC 线程本身要吃 CPU:
+
+- 遍历对象图做可达性分析
+- 拷贝存活对象
+- 整理碎片
+- 这些时间本来可以用来跑业务逻辑
+
+Go 的并发 GC 把大部分工作放到后台线程做,但还是要占 CPU。
+
+### 4. 吞吐量 vs 延迟的矛盾
+
+- **追求高吞吐**:少触发 GC,攒多了一次性清 → 单次暂停更长
+- **追求低延迟**:频繁触发 GC → 暂停短但总 CPU 开销更高
+
+这是 GC 调优里永恒的权衡:
+
+| 收集器 | 侧重 | 特点 |
+|--------|------|------|
+| Parallel GC | 高吞吐 | 暂停长,适合后台批处理 |
+| ZGC | 超低延迟 | 暂停 <1ms,吞吐略低 |
+| G1 | 折中 | 可预测暂停 |
+
+### 5. 内存碎片
+
+不同 GC 策略表现不同:
+
+| 策略 | 碎片情况 |
+|------|----------|
+| 标记-清除 | 有碎片,长时间运行后分配变慢 |
+| 标记-压缩 | 无碎片,但压缩过程要移动对象,暂停更长 |
+| 复制算法 | 无碎片,但浪费一半内存空间 |
+
+碎片多了之后,本来有足够总空闲内存,却分配不出连续的大对象。
+
+### 6. 不可预测性
+
+你无法精确控制何时释放:
+
+```python
+# 你以为这里释放了
+obj = None
+# 但 GC 可能根本不急着回收
+# 文件句柄、数据库连接这种稀缺资源就悬着
+```
+
+所以 GC 语言里经常需要 `try-with-resources`、`with` 语句、析构器配合等机制来手动管理资源释放时机。
+
+### 7. 维护复杂度
+
+GC 的实现本身很复杂——分代阈值怎么设?并发标记和业务线程怎么协调?大对象走哪个分配路径?调优参数一堆,出了问题排查困难。
+
+---
+
+## 一句话总结
+
+> **GC 用 CPU、内存、延迟三样东西,换程序员的脑子。** 值不值取决于场景——绝大多数业务应用完全值得;极端性能敏感的场景(嵌入式、高频交易、游戏引擎核心)就可能不值得。
+
+## 练习题
+
+??? question "题目一:哪些语言有 GC?哪些没有?"
+ ??? success "答案"
+ 有 GC:Java、Go、Python、JavaScript、C#、Ruby、Kotlin、Swift(ARC)
+
+ 无 GC(手动管理):C、C++
+
+ 无 GC(所有权系统):Rust
+
+ 可选 GC:D、Swift
+
+??? question "题目二:引用计数和可达性分析有什么区别?各有什么优缺点?"
+ ??? success "答案"
+ 引用计数:每个对象维护引用计数器,归零即回收。优点是实现简单、回收及时;缺点是无法处理循环引用(A→B→A),且计数器维护有性能开销。Python 用这种。
+
+ 可达性分析:从 GC Root 出发遍历对象图,不可达即为垃圾。优点是能处理循环引用;缺点是遍历耗时,可能需要 STW。Java、Go、JavaScript 用这种。
+
+## 相关链接
+
+- [维基百科:Garbage collection (computer science)](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science))
+- [深入理解 Java 虚拟机 — 周志明](https://book.douban.com/subject/34907498/)
diff --git a/docs/jvm/gc-collectors.md b/docs/jvm/gc-collectors.md
new file mode 100644
index 0000000..9e5668f
--- /dev/null
+++ b/docs/jvm/gc-collectors.md
@@ -0,0 +1,246 @@
+# GC 回收器对比
+
+!!! note "从 Serial 到 ZGC,各回收器的核心差异在于吞吐量与延迟的取舍;G1 取代 CMS 成为主流,因为解决了碎片和可预测暂停问题。"
+
+---
+
+## 一句话比喻
+
+把 Java 堆想象成一栋写字楼:
+
+```
+🏢 Serial GC = 一个保安自己扫
+🏢 Parallel GC = 一群保安一起扫
+🏢 CMS GC = 保安边上班边偷偷扫
+🏢 G1 GC = 按楼层分区,哪脏扫哪
+🏢 ZGC GC = 超级保安,几乎感觉不到
+```
+
+## 各回收器定位
+
+| 回收器 | JDK 版本 | 作用范围 | 核心特点 |
+|--------|---------|---------|---------|
+| **Serial** | 1.0+ | 全堆 | 单线程,STW,最简单 |
+| **Parallel** | 1.2+ | 全堆 | 多线程 STW,追求吞吐量 |
+| **ParNew** | 1.4+ | 年轻代 | 多线程版 Serial,配合 CMS 用 |
+| **CMS** | 1.5+ | 老年代 | 尽量并发,追求低延迟 |
+| **G1** | 1.7+ | 全堆 | 分 Region,可预测暂停 |
+| **ZGC** | 15+ | 全堆 | 暂停 <1ms,超大堆 |
+| **Shenandoah** | 12+ | 全堆 | 并发压缩,低延迟 |
+
+---
+
+## CMS 回收器(Concurrent Mark Sweep)
+
+**核心思想:老年代的垃圾,尽量让 GC 线程和业务线程同时跑。**
+
+### 四个阶段
+
+```mermaid
+sequenceDiagram
+ participant B as 业务线程
+ participant G as GC 线程
+
+ Note over B: 正常运行
+ B->>B: 正常运行
+
+ Note over G: ① 初始标记(极短 STW)
+ G->>G: 标记 GC Roots 直接引用
+
+ Note over B,G: ② 并发标记(不暂停业务)
+ B->>B: 正常运行
+ G->>G: 遍历对象图(耗时最长)
+
+ Note over G: ③ 重新标记(短 STW)
+ G->>G: 修正并发阶段变动
+
+ Note over B,G: ④ 并发清除(不暂停业务)
+ B->>B: 正常运行
+ G->>G: 清除标记为垃圾的对象
+```
+
+### 每个阶段详解
+
+#### ① 初始标记(Initial Mark)⏱ 极短,STW
+
+只标记 GC Roots 直接引用的对象,不做全图遍历,所以很快。
+
+#### ② 并发标记(Concurrent Mark)⏱ 最长,但不暂停业务
+
+从 GC Root 出发,遍历整个对象图。GC 线程和业务线程同时运行。⚠️ 这阶段业务可能产生新的垃圾,标记不到。
+
+#### ③ 重新标记(Remark)⏱ 短,STW
+
+修正并发阶段产生的变动,用了 Incremental Update 算法。
+
+#### ④ 并发清除(Concurrent Sweep)⏱ 长,但不暂停业务
+
+清除标记为垃圾的对象,GC 线程和业务线程同时运行。
+
+### CMS 的四个致命问题
+
+#### 问题 1:浮动垃圾(Floating Garbage)
+
+并发阶段产生的新垃圾,重新标记阶段才发现不了,只能等下次 GC 才能回收。需要保守预留更多老年代空间。
+
+#### 问题 2:内存碎片
+
+CMS 用的是标记-清除,不压缩。久了之后老年代全是碎片:
+
+```
+┌──┬░░┬──┬░░┬──┬░░┬░░┬──┬░░┬──┐
+│▓▓│ │▓▓│ │▓▓│ │ │▓▓│ │▓▓│
+└──┴░░┴──┴░░┴──┴░░┴░░┴──┴░░┴──┘
+```
+
+想分配一个大对象?放不下 → 触发 Full GC(退化为 Serial 的标记-压缩)。**这是最致命的。**
+
+#### 问题 3:Concurrent Mode Failure
+
+GC 还没清完,老年代就满了 → 被迫 STW 做一次 Full GC → 延迟暴增,CMS 的初衷白费了。
+
+#### 问题 4:CPU 敏感
+
+并发阶段抢占 CPU 资源。默认 GC 线程数 = (CPU 核数 + 3) / 4。4 核机器上 GC 吃掉 25% CPU。
+
+---
+
+## G1 回收器(Garbage-First)
+
+**核心思想:把堆切成很多小 Region,哪里垃圾最多先扫哪里。**
+
+### 堆的结构
+
+传统 GC 把堆分成连续的年轻代/老年代,G1 则把堆切成 Region:
+
+```mermaid
+graph TD
+ subgraph 传统布局
+ A[Young Gen] --- B[Old Gen]
+ end
+ subgraph G1 布局
+ C[Region 1: E] --- D[Region 2: E] --- E[Region 3: S] --- F[Region 4: O] --- G[Region 5: H] --- H[Region 6: free]
+ end
+```
+
+```
+G1 布局示例(每个 Region 1~32MB,堆被切成约 2048 个):
+┌────┬────┬────┬────┬────┬────┬────┬────┐
+│ E │ E │ E │ S │ O │ O │ H │free│
+├────┼────┼────┼────┼────┼────┼────┼────┤
+│ O │free│ E │ E │ O │free│ O │ O │
+├────┼────┼────┼────┼────┼────┼────┼────┤
+│free│ E │ O │free│free│ S │ E │free│
+└────┴────┴────┴────┴────┴────┴────┴────┘
+
+E = Eden S = Survivor O = Old H = Humongous(大对象) free = 空闲
+```
+
+### G1 的回收策略
+
+"Garbage-First" = 优先回收垃圾最多的 Region。
+
+每个 Region 的角色是**动态分配**的:
+
+```
+Region #5 角色变化:
+ │
+ ├─→ 这轮是 Eden ← 新对象分配在这里
+ ├─→ 下轮变成 free ← GC 后清空
+ ├─→ 再下轮变成 Old ← 有对象晋升过来
+ └─→ 之后变成 free ← 老年代 GC 后释放
+```
+
+### G1 的核心优势:可预测暂停
+
+```
+调优命令:-XX:MaxGCPauseTime=200ms
+
+G1 自动计算:"我有 200ms 时间,哪些 Region 垃圾最多又能在 200ms 内清完?"
+→ 挑一组 Region 收割
+→ 保证暂停在 200ms 以内
+
+时间轴:
+ ══╪════════╪═══════╪═══════╪═════════
+ 180ms 150ms 160ms 170ms ← 每次都在目标内
+```
+
+### G1 的工作阶段
+
+| 阶段 | 描述 | STW |
+|------|------|-----|
+| 年轻代 GC | Eden 满 → 存活对象复制到 Survivor Region | ✅ 短 |
+| 初始标记 | 标记 GC Roots(搭年轻代 GC 的便车) | ✅ 短 |
+| 并发标记 | 和业务线程并行遍历对象图 | ❌ 不暂停 |
+| 重新标记 | 用 SATB 修正并发阶段变动 | ✅ 短 |
+| 清除 | 清理空 Region,为 Mixed GC 做准备 | ✅ 短 |
+| Mixed GC | 回收年轻代 + 选一批垃圾多的老年代 Region | ✅ 可控 |
+
+### G1 动态调整代大小
+
+```
+默认:
+ -XX:G1NewSizePercent=5 年轻代最小 5%
+ -XX:G1MaxNewSizePercent=60 年轻代最大 60%
+
+场景 1:大量短命对象 → G1 自动扩大年轻代
+┌──────────────────────────────────┐
+│ E E E E E E E E E E E │ O O O O │
+│ 年轻代 (80%) │老年代20% │
+└──────────────────────────────────┘
+
+场景 2:大量长命对象 → G1 自动缩小年轻代
+┌──────────────┬──────────────────────────┐
+│ E E E │ O O O O O O O O O O O O O O O O│
+│ 10% │ 老年代 90% │
+└───────┴────────────────────────────────┘
+```
+
+---
+
+## CMS vs G1 正面对比
+
+| 维度 | CMS | G1 |
+|------|-----|-----|
+| 作用范围 | 仅老年代 | 全堆(年轻代 + 老年代) |
+| 算法 | 标记-清除(不压缩) | 标记-复制 + 标记-压缩 |
+| 内存碎片 | ❌ 有,严重 | ✅ 无(Region 间整理) |
+| 暂停时间 | 不可控 | 可预测(设目标值) |
+| 浮动垃圾 | 有,需预留空间 | 有,但 Mixed GC 兜底 |
+| 大堆表现 | 差(碎片 + 并发慢) | 好(Region 分治) |
+| 小堆表现 | 还行 | 开销略高(Region 管理) |
+| 并发阶段 | 并发标记 + 并发清除 | 并发标记(清除是 STW) |
+| JDK 默认 | JDK 8 可选 | **JDK 9+ 默认** |
+
+### 为什么 CMS 被 G1 取代
+
+```
+CMS 的致命伤:
+ 1. 碎片 → 大对象分配失败 → Full GC → 延迟暴增
+ 2. Concurrent Mode Failure → Full GC → 延迟暴增
+ 3. 不压缩 → 这两个问题无解
+ 4. JDK 9 开始标记为废弃
+
+G1 的解决方式:
+ 1. Region 本身就是压缩的 → 无碎片
+ 2. Mixed GC 主动回收老年代 → 不会等到老年代爆满
+ 3. 可控暂停 → 不会出现意外的长暂停
+ 4. JDK 9+ 默认 GC,生产环境广泛验证
+```
+
+---
+
+## 练习题
+
+??? question "题目一:CMS 的"标记-清除"有什么缺点?为什么 G1 要改成"标记-压缩"?"
+ ??? success "答案"
+ 标记-清除不移动对象,释放后留下碎片。长时间运行后老年代全是碎片,大对象分配失败,被迫触发 Full GC。G1 在 Region 级别做标记-压缩,存活对象复制到新 Region,旧 Region 直接回收,天然无碎片。
+
+??? question "题目二:G1 的"可预测暂停"是怎么实现的?"
+ ??? success "答案"
+ G1 在并发标记结束后,会计算每个 Region 的垃圾比例和回收收益。然后根据用户设定的目标暂停时间(-XX:MaxGCPauseTime),贪心地选择一组 Region 进行 Mixed GC,确保选中的 Region 能在目标时间内清完。
+
+## 相关链接
+
+- [Oracle 官方文档 — G1 Garbage Collector](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html)
+- [Oracle 官方文档 — Concurrent Mark Sweep (CMS) Collector](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html)
diff --git a/docs/jvm/generational-gc.md b/docs/jvm/generational-gc.md
new file mode 100644
index 0000000..29a73af
--- /dev/null
+++ b/docs/jvm/generational-gc.md
@@ -0,0 +1,155 @@
+# Java 分代 GC
+
+!!! note "分代 GC 的核心思想:不同生命周期的对象用不同策略回收——短命的快扫,长寿的慢扫。"
+
+---
+
+## 为什么要分代
+
+研究表明,绝大多数对象都是"朝生夕灭"的(比如方法里的临时变量、请求处理的中间对象),只有少数对象能活很久。分代 GC 就是利用这个规律。
+
+---
+
+## 堆的分代结构
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ Java 堆(Heap) │
+│ │
+│ ┌──────────────────────────┐ ┌─────────────────────┐ │
+│ │ 年轻代 (Young Gen) │ │ 老年代 (Old Gen) │ │
+│ │ │ │ │ │
+│ │ ┌─────┬─────┬────────┐ │ │ │ │
+│ │ │ Eden │ S0 │ S1 │ │ │ │ │
+│ │ │ 80% │ 10% │ 10% │ │ │ Tenured Space │ │
+│ │ └─────┴─────┴────────┘ │ │ (70%) │ │
+│ │ (默认 1/3 堆) │ │ (默认 2/3 堆) │ │
+│ └──────────────────────────┘ └─────────────────────┘ │
+│ │
+│ ┌──────────────────────────────────────────────────┐ │
+│ │ 元空间 (Metaspace) │ │
+│ │ 存类的元数据(JDK 8+ 移到堆外,不归 GC 管) │ │
+│ └──────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────┘
+```
+
+## 年轻代(Young Generation)—— 细分三块
+
+### Eden 区(80%)
+
+新对象都在这里出生。绝大多数对象第一次 GC 就死了。
+
+### Survivor 0 / Survivor 1(各 10%)
+
+也叫 S0、S1,交替使用。活过一次 GC 的对象先搬到这里暂住。
+
+### 运作流程
+
+```mermaid
+graph TD
+ A[新对象分配到 Eden] --> B{Eden 满了?}
+ B -->|否| A
+ B -->|是| C[触发 Minor GC]
+ C --> D[扫描 Eden + S0,标记存活]
+ D --> E[存活对象复制到 S1]
+ E --> F[Eden 和 S0 全部清空]
+ F --> G{对象年龄 ≥ 阈值?}
+ G -->|否| H[留在 Survivor,年龄+1]
+ G -->|是| I[晋升到老年代]
+ H --> A
+```
+
+流程演示:
+
+```
+第 1 步:新对象分配到 Eden
+┌────────┬──────┬──────┐
+│ Eden │ S0 │ S1 │
+│▓▓▓▓▓▓▓│ │ │
+│▓▓▓▓▓▓▓│ │ │
+└────────┴──────┴──────┘
+ ▓ = 新对象
+
+第 2 步:Eden 满了,触发 Minor GC
+ - 扫描 Eden + S0,标记存活对象
+ - 存活对象复制到 S1
+ - Eden 和 S0 全部清空
+
+┌────────┬──────┬──────┐
+│ Eden │ S0 │ S1 │
+│ │ │▓▓ │
+└────────┴──────┴──────┘
+ S1 里是活下来的对象
+
+第 3 步:下次 Minor GC
+ - 扫描 Eden + S1,存活对象复制到 S0
+ - S0 和 Eden 清空
+ - S0 ↔ S1 角色互换
+
+第 4 步:对象每活过一轮 GC,年龄 +1
+ - 年龄达到阈值(默认 15)→ 晋升到老年代
+```
+
+## 老年代(Old Generation)
+
+存放长期存活的对象:
+
+- 年龄达标的对象晋升过来
+- 大对象直接分配在这里(避免在年轻代来回复制)
+- 回收频率低,用标记-清除或标记-压缩
+
+## 触发 GC 的条件
+
+| GC 类型 | 触发时机 | 回收范围 | 算法 | 特点 |
+|---------|---------|---------|------|------|
+| Minor GC | Eden 区满 | Eden + 活跃 Survivor | 复制算法 | 快(几毫秒),频繁 |
+| Major GC / Full GC | 老年代空间不足 | 整个堆(甚至方法区) | 标记-整理/清除 | 慢(几百毫秒~秒级) |
+| Mixed GC | G1 专用,老年代占阈值 | 年轻代 + 部分老年代 Region | 混合 | 折中,可控暂停 |
+
+## 对象晋升流程
+
+```mermaid
+graph TD
+ A[new 对象] --> B[Eden 区]
+ B --> C{Minor GC}
+ C -->|死亡| D[直接回收]
+ C -->|存活| E[进入 Survivor]
+ E --> F{年龄 ≥ 15?}
+ F -->|否| E
+ F -->|是| G[晋升到老年代]
+ G --> H{老年代满?}
+ H -->|否| I[继续存活]
+ H -->|是| J[触发 Major GC / Full GC]
+```
+
+## 为什么这样设计有效
+
+```
+场景:一个 Web 请求处理
+├─ 请求进来了,创建一堆临时对象 → Eden
+├─ 处理完,这些对象没人引用了
+├─ Minor GC 一扫,99% 都是垃圾,直接清掉
+└─ 真正活得久的(连接池、缓存)慢慢晋升到老年代
+
+效率:只扫描 Eden(小区域),速度极快
+ 真正长寿的对象不频繁被扫描
+```
+
+## GC 收集器演进
+
+| JDK 版本 | 年轻代收集器 | 老年代收集器 | 默认 |
+|---------|-------------|-------------|------|
+| 1.x ~ 7 | Serial / Parallel / ParNew | Serial Old / CMS / Parallel Old | Parallel |
+| 8 | Parallel | Parallel Old | Parallel |
+| 9+ | G1(全堆) | G1(全堆) | **G1** |
+| 15+ | ZGC / Shenandoah | ZGC / Shenandoah | 可选 |
+
+## 练习题
+
+??? question "题目一:为什么 Survivor 区要分成 S0 和 S1 两个?"
+ ??? success "答案"
+ 因为复制算法需要一块空闲空间来存放存活对象。如果只有一个 Survivor 区,就没地方复制了。两个 Survivor 区交替使用:Minor GC 时,把 Eden 和当前 Survivor 中存活的对象复制到另一个 Survivor,然后清空前者。这样每次都有一块干净的空间可用。
+
+??? question "题目二:对象年龄阈值默认是 15,为什么不能设太小?"
+ ??? success "答案"
+ 如果阈值太小,很多对象还没来得及死亡就被晋升到老年代,导致老年代快速增长,触发频繁的 Major GC。Major GC 比 Minor GC 慢得多。默认 15 是经验值,大多数短命对象在年轻代就被回收了,少数真正长寿的对象才晋升。年龄存储在对象头的 Mark Word 中,占 4 位,所以最大值就是 15。
diff --git a/docs/jvm/index.md b/docs/jvm/index.md
new file mode 100644
index 0000000..e8aadb7
--- /dev/null
+++ b/docs/jvm/index.md
@@ -0,0 +1,13 @@
+# JVM 与垃圾回收
+
+!!! note "深入理解 Java 虚拟机的内存管理机制"
+
+本章系统整理了垃圾回收(GC)的核心知识,从基础概念到高级算法,涵盖:
+
+| 文档 | 主题 | 关键词 |
+|------|------|--------|
+| [垃圾回收基础](garbage-collection.md) | GC 概念、无 GC 的语言、GC 的代价 | 自动内存管理 |
+| [内存碎片与整理](memory-fragmentation.md) | 碎片产生原因、标记-清除/压缩/复制三种整理方式 | 碎片、Mark-Sweep、Mark-Compact |
+| [分代 GC](generational-gc.md) | Java 分代结构、Eden/Survivor/老年代、对象晋升流程 | 分代、Minor GC、Major GC |
+| [GC 回收器对比](gc-collectors.md) | Serial/Parallel/CMS/G1/ZGC 对比,重点 CMS 与 G1 | CMS、G1、可预测暂停 |
+| [GC Roots 与三色标记](tri-color-marking.md) | GC Root 类型、可达性分析、三色标记法及并发问题 | GC Root、漏标、SATB |
diff --git a/docs/jvm/memory-fragmentation.md b/docs/jvm/memory-fragmentation.md
new file mode 100644
index 0000000..f0c4224
--- /dev/null
+++ b/docs/jvm/memory-fragmentation.md
@@ -0,0 +1,159 @@
+# 内存碎片与整理方式
+
+!!! note "内存碎片是对象随机分配/释放后产生的空洞,主流 GC 用标记-清除、标记-压缩、复制算法三种方式应对。"
+
+---
+
+## 什么是内存碎片
+
+程序运行时,内存就像一排格子,每次 `new` 对象就占几个格子。释放后留下空洞,如果空洞分散,就会出现**总空闲空间够用,但放不下一个大对象**的情况。
+
+### 碎片产生过程
+
+初始分配:
+
+```
+┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
+│A │A │B │B │C │C │C │D │D │D │E │E │F │F │F │G │
+└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
+```
+
+释放 B、D、F 后:
+
+```
+┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
+│A │A │░░│░░│C │C │C │░░│░░│░░│E │E │░░│░░│░░│G │
+└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
+ ░░ = 空闲碎片
+```
+
+总空闲 8 格,但没有一块连续。想分配一个 4 格的对象?**放不下。** 这就是内存碎片。
+
+---
+
+## 三种主流整理方式
+
+### 1. 标记-清除(Mark-Sweep)
+
+不做整理,只标记死对象然后就地释放。
+
+```mermaid
+graph LR
+ A[标记阶段] -->|从 GC Root 遍历| B[标记所有存活对象]
+ B --> C[清除阶段]
+ C -->|释放未标记对象| D[就地释放]
+ D --> E[产生碎片]
+```
+
+流程演示:
+
+```
+标记阶段:标记哪些还活着
+┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
+│A │A │B │B │C │C │C │D │D │D │E │E │F │F │F │G │
+│✓ │✓ │ │ │✓ │✓ │✓ │ │ │ │✓ │✓ │ │ │ │✓ │
+└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
+
+清除后:
+┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
+│A │A │░░│░░│C │C │C │░░│░░│░░│E │E │░░│░░│░░│G │
+└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
+```
+
+✅ 速度最快,只扫描一遍
+❌ **碎片原封不动,碎片越来越多**
+
+### 2. 标记-压缩(Mark-Compact)
+
+标记后把所有活对象**挤到一端**,消除空洞。
+
+```mermaid
+graph LR
+ A[标记阶段] --> B[标记存活对象]
+ B --> C[压缩阶段]
+ C -->|活对象向一端移动| D[消除碎片]
+ D --> E[更新引用]
+```
+
+流程演示:
+
+```
+标记完成:
+┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
+│A │A │░░│░░│C │C │C │░░│░░│░░│E │E │░░│░░│░░│G │
+│✓ │✓ │ │ │✓ │✓ │✓ │ │ │ │✓ │✓ │ │ │✓ │
+└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
+
+压缩 — 活对象向左移动:
+ A → 移到 [0]
+ C → 紧跟 A 放到 [2]
+ E → 紧跟 C 放到 [5]
+ G → 紧跟 E 放到 [7]
+
+整理后:
+┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
+│A │A │C │C │C │E │E │G │░░│░░│░░│░░│░░│░░│░░│░░│
+└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
+ 已用(紧凑) 空闲(连续一大块)
+```
+
+✅ **碎片彻底消除**
+❌ 要移动对象,更新所有指向它的引用,**暂停时间长**
+
+### 3. 复制算法(Copying)
+
+把内存切成两半(From / To),每次只用一半,GC 时把活对象复制到另一半。
+
+```mermaid
+graph LR
+ A[From 区使用中] --> B[GC 触发]
+ B --> C[扫描存活对象]
+ C --> D[复制到 To 区]
+ D --> E[From/To 角色互换]
+ E --> A
+```
+
+流程演示:
+
+```
+From 区(当前使用) To 区(空闲备用)
+┌──┬──┬──┬──┬──┬──┬──┐ ┌──┬──┬──┬──┬──┬──┬──┐
+│A │A │░░│C │C │░░│E │ │ │ │ │ │ │ │ │
+│✓ │✓ │ │✓ │✓ │ │✓ │ │ │ │ │ │ │ │ │
+└──┴──┴──┴──┴──┴──┴──┘ └──┴──┴──┴──┴──┴──┴──┘
+```
+
+GC 时,把活对象按顺序复制到 To 区:
+
+```
+From 区(废弃) To 区(新的活跃区)
+┌──┬──┬──┬──┬──┬──┬──┐ ┌──┬──┬──┬──┬──┬──┬──┐
+│A │A │░░│C │C │░░│E │ │A │A │C │C │E │░░│░░│
+└──┴──┴──┴──┴──┴──┴──┘ └──┴──┴──┴──┴──┴──┴──┘
+ ↑ 全部作废 紧凑排列,后面全是空闲
+```
+
+✅ 分配极快(指针一推就行),**天然无碎片**
+❌ **浪费一半内存**,存活对象多时复制成本高
+
+---
+
+## 总结对比
+
+| 算法 | 速度 | 碎片 | 内存代价 | 适用场景 |
+|------|------|------|----------|----------|
+| 标记-清除 | ⚡ 快 | ❌ 有碎片 | 无额外 | CMS 老年代 |
+| 标记-压缩 | 🐢 慢 | ✅ 无碎片 | 无额外 | G1 老年代 |
+| 复制算法 | ⚡ 快 | ✅ 无碎片 | 浪费 50% | 年轻代(死亡率高) |
+
+实际的 GC 实现通常**混合使用**——比如 Java 的分代 GC:年轻代用复制算法(对象死亡率高,复制量小),老年代用标记-压缩(对象存活久,复制成本太高)。
+
+## 练习题
+
+??? question "题目一:为什么复制算法通常只用于年轻代?"
+ ??? success "答案"
+ 因为研究表明绝大多数对象都是"朝生夕灭"的,年轻代中每次 Minor GC 后存活对象通常不到 10%。复制算法只需要复制极少量存活对象,效率很高。而老年代对象存活率高,复制成本太大,所以用标记-压缩。
+
+??? question "题目二:标记-清除为什么会导致碎片越来越多?"
+ ??? success "答案"
+ 标记-清除只是就地释放死对象的内存,不会移动任何活对象。随着程序不断分配和释放,空洞越来越分散,连续空闲空间越来越小。大对象可能因为找不到连续空间而触发 Full GC 甚至 OOM。
diff --git a/docs/jvm/tri-color-marking.md b/docs/jvm/tri-color-marking.md
new file mode 100644
index 0000000..3b6d92b
--- /dev/null
+++ b/docs/jvm/tri-color-marking.md
@@ -0,0 +1,203 @@
+# GC Roots 与三色标记法
+
+!!! note "GC Roots 是可达性分析的起点,三色标记法是现代低延迟 GC 的核心标记算法,通过写屏障解决并发时的漏标问题。"
+
+---
+
+## GC Roots
+
+GC Root 是垃圾回收的**起点**。从这些对象出发,能走到的就是活的,走不到的就是垃圾。
+
+### 七种 GC Root
+
+| 类型 | 说明 | 示例 |
+|------|------|------|
+| 虚拟机栈中的引用 | 每个线程栈帧中的局部变量 | 方法里的 `User user = new User()` |
+| 静态变量 | 类的 static 引用 | `private static AppConfig instance` |
+| 常量池引用 | static final 引用的对象 | `private static final String NAME` |
+| JNI 引用 | 本地方法中持有的 Java 对象 | `native void method(Buffer buf)` |
+| 活跃线程 | 每个正在运行的线程 | `Thread` 对象 |
+| 同步锁 | 被 `synchronized` 持有的对象 | `synchronized (sharedLock)` |
+| Class 对象 | 已加载类对应的 Class 对象 | `User.class` |
+
+### 可达性分析图
+
+```mermaid
+graph TD
+ R1[虚拟机栈局部变量] --> A[对象 A]
+ A --> B[对象 B]
+ B --> C[对象 C]
+ B --> D[对象 D]
+
+ R2[静态变量] --> E[对象 E]
+ E --> F[对象 F]
+
+ R3[JNI 引用] --> G[对象 G]
+
+ R4[活跃线程] --> H[对象 H]
+ R5[同步锁] --> I[对象 I]
+ R6[Class 对象] --> J[对象 J]
+
+ K[对象 K] -.->|无引用链| R1
+ L[对象 L] -.->|仅被 K 引用| K
+
+ style K fill:#ff6b6b,color:#fff
+ style L fill:#ff6b6b,color:#fff
+ style R1 fill:#51cf66,color:#fff
+ style R2 fill:#51cf66,color:#fff
+ style R3 fill:#51cf66,color:#fff
+ style R4 fill:#51cf66,color:#fff
+ style R5 fill:#51cf66,color:#fff
+ style R6 fill:#51cf66,color:#fff
+```
+
+K 和 L 没有任何 GC Root 能到达 → 垃圾,可回收。
+
+---
+
+## 三色标记法
+
+垃圾回收中用来标记对象存活状态的算法,是 G1、ZGC、Go 等现代 GC 的核心。
+
+### 三种颜色
+
+| 颜色 | 含义 | 最终状态 |
+|------|------|---------|
+| ⚪ 白色 | 还没扫描到的 | 垃圾,可回收 |
+| 🔘 灰色 | 扫描过了,但它引用的对象还没扫 | 等待继续扫描 |
+| ⚫ 黑色 | 扫描过了,它引用的对象也都扫了 | 存活 |
+
+### 完整标记过程
+
+用一个例子演示:
+
+```
+对象关系:
+GC Roots → A → B → C → G
+ ↓ ↓
+ D (G 仅被 C 引用)
+GC Roots → E → F
+```
+
+**初始状态**:GC Root 直接引用的对象标灰,其余全白。
+
+```
+⚫ A ⚪ B ⚪ C ⚪ D ⚫ E ⚪ F ⚪ G
+```
+
+**扫描 A**:A 有引用 → B,A 变黑,B 变灰。
+
+```
+⚫ A 🔘 B ⚪ C ⚪ D ⚫ E ⚪ F ⚪ G
+```
+
+**扫描 B**:B 有引用 → C、D,B 变黑,C 和 D 变灰。
+
+```
+⚫ A ⚫ B 🔘 C 🔘 D ⚫ E ⚪ F ⚪ G
+```
+
+**扫描 C**:C 有引用 → G,C 变黑,G 变灰。
+
+```
+⚫ A ⚫ B ⚫ C 🔘 D ⚫ E ⚪ F 🔘 G
+```
+
+**扫描 D、F、G**:无更多引用,全部变黑。
+
+```
+⚫ A ⚫ B ⚫ C ⚫ D ⚫ E ⚫ F ⚫ G
+```
+
+所有对象变黑,标记完成,无白色对象可回收。
+
+```mermaid
+graph LR
+ subgraph "① 初始状态"
+ A1[⚫A] --> B1[⚪B]
+ E1[⚫E] --> F1[⚪F]
+ end
+ subgraph "② 扫描 A"
+ A2[⚫A] --> B2[🔘B]
+ E2[⚫E] --> F2[⚪F]
+ end
+ subgraph "③ 扫描 B"
+ A3[⚫A] --> B3[⚫B]
+ B3 --> C3[🔘C]
+ B3 --> D3[🔘D]
+ E3[⚫E] --> F3[⚪F]
+ end
+```
+
+### 流程总结
+
+```mermaid
+graph TD
+ A[GC Root 标灰] --> B[取一个灰色对象]
+ B --> C[扫描它的引用]
+ C --> D[灰变黑,引用的白变灰]
+ D --> E{还有灰色?}
+ E -->|是| B
+ E -->|否| F[剩余白色 = 垃圾]
+```
+
+---
+
+## 并发标记的两大问题
+
+三色标记通常和业务线程**并发执行**,会产生问题:
+
+### 问题一:漏标 — 活对象被误判为垃圾 💀
+
+**同时**满足两个条件才会漏标:
+
+1. 存在黑色对象到白色对象的**新引用**
+2. 所有灰色对象到该白色对象的引用都被**删除**
+
+```mermaid
+sequenceDiagram
+ participant GC as GC 线程
+ participant App as 业务线程
+
+ Note over GC: ⚫A 🔘B ⚪C
A→C, B→C
+ App->>App: A 删除 A→C
+ App->>App: B 新增 B→C
+ Note over GC: A 是黑色,不会再被扫描
C 是白色 → 误判为垃圾!💥
+```
+
+### 问题二:多标 — 死对象被当成活的(浮动垃圾)😅
+
+并发期间某个对象变成垃圾,但标记阶段已经过了它 → 这次回收不了。下次 GC 就能回收,**不致命**,只是多占一轮内存。
+
+### 解决漏标
+
+| 方案 | 策略 | 使用者 | 优缺点 |
+|------|------|--------|--------|
+| **增量更新** | 黑色新增引用时,把它变回灰色,重新扫描 | CMS | 缺点:Remark 阶段可能较长 |
+| **SATB**(原始快照) | 删除引用时,记录被删除的对象,标记它 | G1 | 缺点:可能多标浮动垃圾,但不会漏标 |
+| **染色指针 + 读屏障** | 硬件级别解决 | ZGC | 暂停 <1ms |
+
+### 对比总结
+
+| | 传统标记 | 三色标记 |
+|---|---------|---------|
+| 状态 | 二元(已访问/未访问) | 三态(白/灰/黑) |
+| 并发能力 | 无法区分"还没扫到"和"确认是垃圾" | 精确追踪进度,写屏障维护一致性 |
+| 使用场景 | 单线程 GC | 并发 GC(G1、ZGC、Go) |
+
+---
+
+## 练习题
+
+??? question "题目一:为什么多标(浮动垃圾)不致命,但漏标会导致程序崩溃?"
+ ??? success "答案"
+ 多标只是把本该回收的对象多留了一轮,下次 GC 就能回收,影响只是多占一点内存。漏标则相反——活对象被当成垃圾回收了,程序还在用这个对象但内存已经被释放,导致空指针、数据损坏甚至 JVM 崩溃。
+
+??? question "题目二:G1 的 SATB 和 CMS 的增量更新有什么区别?"
+ ??? success "答案"
+ 增量更新在"新增引用"时拦截:黑色对象新增了对白色对象的引用,就把黑色变灰重新扫描。SATB 在"删除引用"时拦截:灰色对象删除了对白色对象的引用,就把被删除的白色对象记录下来,在标记结束时强制标灰。G1 选 SATB 是因为删除引用更常见(对象字段赋 null),拦截成本更低。
+
+## 相关链接
+
+- [深入理解 Java 虚拟机 — 三色标记与写屏障](https://book.douban.com/subject/34907498/)
+- [Oracle — ZGC: A Scalable Low-Latency Garbage Collector](https://docs.oracle.com/en/java/javase/21/gctuning/z-garbage-collector.html)
diff --git a/mkdocs.yml b/mkdocs.yml
index 4b83bc9..e80aa49 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -120,6 +120,13 @@ nav:
- HeavyKeeper: algorithm/heavykeeper.md
- 二叉树的遍历: algorithm/binary-tree-traversal.md
- 队列: algorithm/queue.md
+ - JVM:
+ - jvm/index.md
+ - 垃圾回收基础: jvm/garbage-collection.md
+ - 内存碎片与整理: jvm/memory-fragmentation.md
+ - 分代 GC: jvm/generational-gc.md
+ - GC 回收器对比: jvm/gc-collectors.md
+ - GC Roots 与三色标记: jvm/tri-color-marking.md
- 七牛云:
- qiniu-cloud/index.md
- 前后端架构: qiniu-cloud/architecture.md