vault backup: 2026-06-07 12:14:39
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
---
|
||||
tags:
|
||||
- Go
|
||||
- golang
|
||||
- go原理深入
|
||||
- 内存管理
|
||||
tags: [go, golang, go-principle, garbage-collection]
|
||||
create time: 2026-06-07 16:00
|
||||
---
|
||||
|
||||
# 垃圾回收
|
||||
# 垃圾回收算法
|
||||
|
||||
## 1. 什么是GC
|
||||
## 概述
|
||||
|
||||
本文从 GC 发展史出发,深入解析 Go 的并发三色标记法 + 混合写屏障机制。涵盖引用计数、标记清除、复制法等经典算法对比,以及插入写屏障、删除写屏障、混合写屏障的原理和演进。这是理解 Go 为什么能做到"低停顿、高吞吐"GC 的核心篇章。
|
||||
|
||||
> [!question] ❓ 思考
|
||||
> 为什么 Go 不采用 Java 那样的分代 GC?三色标记在并发的情况下怎么保证不会错误回收还在使用的对象?写屏障是如何在不暂停程序的情况下做到这一点的?
|
||||
|
||||
---
|
||||
|
||||
GC的全称是 Garbage Collection,字面意思是垃圾回收,其实可以理解为垃圾内存回收。GC是编程语言实现的一种自动内存管理机制,用来找到程序中不再使用的那些“垃圾”内存,然后把它们清理掉,让这些内存重新可用。这里所说的“垃圾”内存更确切一点说其实是堆上的不再使用的内存,因为栈上的内存是由编译器自动分配和释放的,不需要GC参与
|
||||
|
||||
@@ -235,13 +239,23 @@ func main() {
|
||||
|
||||
* 黑色:已被垃圾收集器访问到的对象,且其引用都已被扫描到,黑色对象中任何一个指针都不可能直接指向白色对象
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
White["白色<br/>未访问 / 潜在垃圾"] -->|被根扫描发现| Gray["灰色<br/>已访问,待扫描子对象"]
|
||||
Gray -->|子对象全部扫描完| Black["黑色<br/>已完全扫描"]
|
||||
Black -.不可能指向.-> White
|
||||
style White fill:#fff9c4
|
||||
style Gray fill:#ffe0b2
|
||||
style Black fill:#cfd8dc
|
||||
```
|
||||
|
||||
标记过程如下:
|
||||
|
||||
1. **初始状态**:所有对象都是白色的
|
||||
|
||||
2. **扫描根对象**:从根对象开始扫描,将所有可达对象标记为灰色,并放入待处理集合中
|
||||
|
||||
3. **处理灰色对象**:从待处理集合中取出灰色对象,将它们引用的对象标记为灰色,并将这些新标记的对象加入待处理集合中,同时将自身标记为黑色。
|
||||
3. **处理灰色对象**:从待处理集合中取出灰色对象,将它们引用的对象标记为灰色,同时将自身标记为黑色。
|
||||
|
||||
4. **重复扫描**:重复第3步,直到待处理集合为空。此时,所有白色对象都是不可达的垃圾对象,可以进行回收
|
||||
|
||||
@@ -313,6 +327,11 @@ func main() {
|
||||
|
||||
* 灰色对象与它之间的可达关系的白色对象遭到破坏(灰色对象同时丢失了该白色对象的引用)
|
||||
|
||||
> [!warning] ⚠️ 核心矛盾
|
||||
> 并发三色标记的核心问题就是:用户程序(Mutator)在运行,GC 也在运行。用户程序的指针修改可能在 GC 扫描的间隙"偷偷"把一个黑色对象和白色对象连起来,而灰色对象恰好断开了对同一个白色对象的引用——这就导致白色对象被误杀。
|
||||
>
|
||||
> 解决方案只有一个:**引入屏障技术**。
|
||||
|
||||
### **5.5 屏障技术**
|
||||
|
||||
#### **5.5.1 强弱三色不变性**
|
||||
@@ -752,6 +771,13 @@ go语言在Go 1.7 之前其实就使用的是 插入写屏障(Dijkstra Write b
|
||||
|
||||
**混合写屏障模式下,利用删除写屏障避免了插入写屏障的STW问题(全部三色标记扫描之后,要STW对栈重新进行三色标记扫描),又利用插入写屏障避免了删除写屏障的STW问题(使用删除写屏障之前需要STW垃圾扫描整个栈空间,获取快照,把所有的堆对象都处于灰色保护中),这样就完美解决了屏障技术带来的STW问题**
|
||||
|
||||
> [!tip] 💡 理解要点
|
||||
> 混合写屏障的精妙之处在于:它不是简单地把两种屏障叠加,而是巧妙地利用了两者的互补性——
|
||||
> - 删除写屏障保证了"旧引用标灰",避免灰色到白色的路径断裂
|
||||
> - 插入写屏障保证了"新引用也标灰",避免不需要 STW 重扫栈
|
||||
>
|
||||
> 两者结合,GC 全程无需全局 STW!
|
||||
|
||||
上面只是从感官上分析了插入写屏障和删除写屏障的结合,解决了STW的问题,但其实混合写屏障不仅仅是做了这两点
|
||||
|
||||
Go 在Go V1.8版本时候为了简化 GC 的流程,同时减少标记终止阶段的重扫成本,将 Dijkstra 插入屏障和 Yuasa 删除屏障进行混合,引入了混合写屏障机制(hybrid write barrier)。
|
||||
@@ -909,5 +935,12 @@ GO语言GC总体上来说是采用的并行三色标记法+混合写屏障机制
|
||||
|
||||
3. 混合写屏障扫描栈的方式是逐个暂停扫描的,不需要STW
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hzh/GolangStar/Go语言原理/memory management原理]] — 内存管理三层架构
|
||||
- [[hzh/GolangStar/Go语言原理/gmp调度原理]] — sysmon 线程如何触发 GC
|
||||
- [[hzh/GolangStar/Go语言原理/逃逸分析]] — 哪些对象会分配到堆上被 GC
|
||||
- [[hzh/GolangStar/Go面试题库/垃圾回收面试题]] — GC 相关高频面试题
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user