2025-11-30 · garbage-collection
【Garbage Collection Series】Java 垃圾回收机制详解
深入解析 Java 虚拟机 (JVM) 的垃圾回收算法,包括分代收集理论、CMS、G1 以及新一代的 ZGC 和 Shenandoah。
Go 语言的 GC 设计哲学与 Java 截然不同。Go 专注于低延迟 (Low Latency),牺牲了一部分吞吐量来换取极短的 STW (Stop The World) 时间。Go 1.5 引入了并发标记,Go 1.8 引入了混合写屏障,使得 STW 时间通常在微秒级别。
Go 使用并发的三色标记清除算法。
在并发标记过程中,用户线程 (Mutator) 可能会修改引用关系,导致漏标(即把活跃对象误判为垃圾)。漏标发生必须同时满足两个条件: 1. 条件 1: 黑色对象引用了一个白色对象。 2. 条件 2: 灰色对象与该白色对象之间的引用关系被破坏(删除)。
为了防止漏标,必须破坏上述两个条件之一。这就是写屏障的作用。
Go 1.8 之前使用的是 Dijkstra 写屏障或 Yuasa 写屏障,Go 1.8 引入了混合写屏障,结合了两者的优点。
Go 结合了两者,极大地减少了 STW 时间(不再需要 STW 重新扫描栈)。
混合写屏障伪代码:
// slot 是指针在内存中的地址
// ptr 是要写入的新值
writePointer(slot, ptr):
shade(*slot) // 1. 删除写屏障:将旧值置灰 (保护旧路径)
shade(ptr) // 2. 插入写屏障:将新值置灰 (保护新路径)
*slot = ptr // 3. 更新指针Go GC 的触发不仅仅依赖于固定阈值,而是基于反馈控制算法,称为 GC Pacing。
GOGC 环境变量控制了下一次 GC
的触发堆大小。默认值为 100。 \[
\text{NextGC} = \text{HeapMarked} + \text{HeapMarked} \times
\frac{\text{GOGC}}{100} \]
HeapMarked (上次 GC 存活量) 为
10MB,GOGC=100,则下次 GC 在堆达到 20MB
时触发。为了防止在并发标记期间,用户线程分配内存的速度超过 GC 标记的速度,Go 引入了辅助标记。 * 机制: 分配内存过快的 Goroutine 会被”征用”一部分 CPU 时间来协助进行标记工作。 * 效果: 这是一个背压 (Backpressure) 机制,限制了高分配速率的 Goroutine。
在 Go 1.25 中,Go 团队引入了一个代号为 Green
Tea 的新 GC 实现(可通过
GOEXPERIMENT=greenteagc
开启)。这是对标记阶段的重大重构,旨在解决现代硬件上的”微架构灾难”。
传统的 GC 标记算法(Graph Flood / Mark-Sweep)在现代 CPU 上效率极低: * 随机内存访问: 追踪指针意味着在堆内存中随机跳转。 * Cache Miss: 这种随机性导致 CPU 缓存命中率极低。 * 流水线停顿: CPU 必须等待内存数据加载,导致大量指令周期被浪费(Stall)。 * NUMA 问题: 跨核心/跨插槽的内存访问延迟进一步加剧了性能损耗。
Go 团队发现,GC 标记阶段约 35% 的时间是纯粹的内存等待(Memory Stall)。
Green Tea GC 的核心思想是:Work with pages, not objects. (处理页,而不是对象)。
seen:
表示该对象已被其他对象引用(发现)。scanned: 表示该对象已被扫描过。Green Tea 利用”懒惰”(Laziness)来累积工作量,从而实现批量处理。
seen 位。seen=1 且 scanned=0 的对象。scanned 位。这种机制使得 GC 能够在同一个页上一次性处理多个对象,极大地提高了空间局部性 (Spatial Locality)。
由于 Green Tea 将工作规整化为对页的处理,这使得利用 SIMD 指令集(如 AVX-512)成为可能。
Green Tea 使用 AVX-512 指令集并行处理页的元数据。以下是硬件加速的逻辑步骤:
seen 和 scanned 位图加载到 512
位寄存器中。GFNI (Galois Field New Instructions)
指令集中的 VGF2P8AFFINEQB 指令。1 会膨胀为
111。Pointer/Scalar
位图(指示内存中哪些位置是指针)。ActivePointers
位图,直接从内存中批量加载所有指针,放入缓冲区。if is_pointer 判断被位运算取代。| 特性 | 传统 GC (Graph Flood) | Green Tea GC |
|---|---|---|
| 工作单元 | 对象 (Object) | 页 (Page, 8KB) |
| 遍历顺序 | 深度优先 (DFS) | 广度优先 (BFS) |
| 内存访问 | 随机跳跃 (Random) | 线性扫描 (Linear) |
| 硬件利用 | 差 (Cache Miss 高) | 优 (Cache Friendly + SIMD) |
| CPU 开销 | 基准 | 降低 10% - 40% |
Green Tea 标志着 GC 算法从纯软件算法向软硬协同优化的转变。它不再把 CPU 视为黑盒,而是针对现代处理器的缓存层级和向量指令进行了深度定制。
Go GC 的核心在于低延迟。 1. 并发三色标记: 减少 STW。 2. 混合写屏障: 消除栈重扫的 STW,允许极短的停顿。 3. GC Pacing: 动态调整 GC 频率和辅助标记力度,平衡吞吐量和延迟。 4. Green Tea (新): 通过页级扫描和向量化指令,进一步压榨硬件性能。
如果你想了解 Go GC 与调度器如何协作(Mark Assist 抢占、STW 协调、P 的角色),可以读 Go 调度器深度拆解。
把当前热点继续串成多页阅读,而不是停在单篇消费。
2025-11-30 · garbage-collection
深入解析 Java 虚拟机 (JVM) 的垃圾回收算法,包括分代收集理论、CMS、G1 以及新一代的 ZGC 和 Shenandoah。
2025-11-30 · garbage-collection
深入解析 Python (CPython) 的内存管理,包括引用计数机制和分代循环垃圾收集器。
2025-11-30 · garbage-collection
探索垃圾回收技术的最新进展,包括 ZGC 的染色指针、读屏障技术以及 AI 驱动的参数调优。
2025-11-30 · garbage-collection
多维度对比主流编程语言的内存管理策略,分析吞吐量、延迟与开发效率的权衡。
Go 语言的 GC 设计哲学与 Java 截然不同。Go 专注于低延迟 (Low Latency),牺牲了一部分吞吐量来换取极短的 STW (Stop The World) 时间。Go 1.5 引入了并发标记,Go 1.8 引入了混合写屏障,使得 STW 时间通常在微秒级别。
Go 使用并发的三色标记清除算法。
在并发标记过程中,用户线程 (Mutator) 可能会修改引用关系,导致漏标(即把活跃对象误判为垃圾)。漏标发生必须同时满足两个条件: 1. 条件 1: 黑色对象引用了一个白色对象。 2. 条件 2: 灰色对象与该白色对象之间的引用关系被破坏(删除)。
为了防止漏标,必须破坏上述两个条件之一。这就是写屏障的作用。
Go 1.8 之前使用的是 Dijkstra 写屏障或 Yuasa 写屏障,Go 1.8 引入了混合写屏障,结合了两者的优点。
Go 结合了两者,极大地减少了 STW 时间(不再需要 STW 重新扫描栈)。
混合写屏障伪代码:
// slot 是指针在内存中的地址
// ptr 是要写入的新值
writePointer(slot, ptr):
shade(*slot) // 1. 删除写屏障:将旧值置灰 (保护旧路径)
shade(ptr) // 2. 插入写屏障:将新值置灰 (保护新路径)
*slot = ptr // 3. 更新指针Go GC 的触发不仅仅依赖于固定阈值,而是基于反馈控制算法,称为 GC Pacing。
GOGC 环境变量控制了下一次 GC
的触发堆大小。默认值为 100。 \[
\text{NextGC} = \text{HeapMarked} + \text{HeapMarked} \times
\frac{\text{GOGC}}{100} \]
HeapMarked (上次 GC 存活量) 为
10MB,GOGC=100,则下次 GC 在堆达到 20MB
时触发。为了防止在并发标记期间,用户线程分配内存的速度超过 GC 标记的速度,Go 引入了辅助标记。 * 机制: 分配内存过快的 Goroutine 会被”征用”一部分 CPU 时间来协助进行标记工作。 * 效果: 这是一个背压 (Backpressure) 机制,限制了高分配速率的 Goroutine。
在 Go 1.25 中,Go 团队引入了一个代号为 Green
Tea 的新 GC 实现(可通过
GOEXPERIMENT=greenteagc
开启)。这是对标记阶段的重大重构,旨在解决现代硬件上的”微架构灾难”。
传统的 GC 标记算法(Graph Flood / Mark-Sweep)在现代 CPU 上效率极低: * 随机内存访问: 追踪指针意味着在堆内存中随机跳转。 * Cache Miss: 这种随机性导致 CPU 缓存命中率极低。 * 流水线停顿: CPU 必须等待内存数据加载,导致大量指令周期被浪费(Stall)。 * NUMA 问题: 跨核心/跨插槽的内存访问延迟进一步加剧了性能损耗。
Go 团队发现,GC 标记阶段约 35% 的时间是纯粹的内存等待(Memory Stall)。
Green Tea GC 的核心思想是:Work with pages, not objects. (处理页,而不是对象)。
seen:
表示该对象已被其他对象引用(发现)。scanned: 表示该对象已被扫描过。Green Tea 利用”懒惰”(Laziness)来累积工作量,从而实现批量处理。
seen 位。seen=1 且 scanned=0 的对象。scanned 位。这种机制使得 GC 能够在同一个页上一次性处理多个对象,极大地提高了空间局部性 (Spatial Locality)。
由于 Green Tea 将工作规整化为对页的处理,这使得利用 SIMD 指令集(如 AVX-512)成为可能。
Green Tea 使用 AVX-512 指令集并行处理页的元数据。以下是硬件加速的逻辑步骤:
seen 和 scanned 位图加载到 512
位寄存器中。GFNI (Galois Field New Instructions)
指令集中的 VGF2P8AFFINEQB 指令。1 会膨胀为
111。Pointer/Scalar
位图(指示内存中哪些位置是指针)。ActivePointers
位图,直接从内存中批量加载所有指针,放入缓冲区。if is_pointer 判断被位运算取代。| 特性 | 传统 GC (Graph Flood) | Green Tea GC |
|---|---|---|
| 工作单元 | 对象 (Object) | 页 (Page, 8KB) |
| 遍历顺序 | 深度优先 (DFS) | 广度优先 (BFS) |
| 内存访问 | 随机跳跃 (Random) | 线性扫描 (Linear) |
| 硬件利用 | 差 (Cache Miss 高) | 优 (Cache Friendly + SIMD) |
| CPU 开销 | 基准 | 降低 10% - 40% |
Green Tea 标志着 GC 算法从纯软件算法向软硬协同优化的转变。它不再把 CPU 视为黑盒,而是针对现代处理器的缓存层级和向量指令进行了深度定制。
Go GC 的核心在于低延迟。 1. 并发三色标记: 减少 STW。 2. 混合写屏障: 消除栈重扫的 STW,允许极短的停顿。 3. GC Pacing: 动态调整 GC 频率和辅助标记力度,平衡吞吐量和延迟。 4. Green Tea (新): 通过页级扫描和向量化指令,进一步压榨硬件性能。
如果你想了解 Go GC 与调度器如何协作(Mark Assist 抢占、STW 协调、P 的角色),可以读 Go 调度器深度拆解。
把当前热点继续串成多页阅读,而不是停在单篇消费。
2025-11-30 · garbage-collection
深入解析 Java 虚拟机 (JVM) 的垃圾回收算法,包括分代收集理论、CMS、G1 以及新一代的 ZGC 和 Shenandoah。
2025-11-30 · garbage-collection
深入解析 Python (CPython) 的内存管理,包括引用计数机制和分代循环垃圾收集器。
2025-11-30 · garbage-collection
探索垃圾回收技术的最新进展,包括 ZGC 的染色指针、读屏障技术以及 AI 驱动的参数调优。
2025-11-30 · garbage-collection
多维度对比主流编程语言的内存管理策略,分析吞吐量、延迟与开发效率的权衡。