Go的垃圾回收(GC)可以想象成一种自动的清洁工,它的工作是在你的Go程序运行时不断地清理不再需要的数据,从而防止内存被无用数据占满。这个过程大致可以比作打扫房间:首先要确定哪些东西是你还需要的(标记阶段),然后将不需要的东西扔掉(清除阶段)。让我们通俗地理解一下这个过程:
打扫前的准备(标记阶段)
想象一下,你的房间里有很多盒子(内存中的对象),有些盒子里装的是你需要的东西,有些则是不需要的。但在开始打扫之前,你并不知道哪些是需要的。所以,你开始检查每个盒子(标记活动对象):
- 你首先检查那些容易判断的,比如手边的东西(根对象),然后是手边东西指向的东西(从根对象可达的对象)。
- 当你检查一个盒子时,你用不同的标记笔记下这是一个"需要保留"的盒子。
- 这个过程中,你不停地移动,同时保持你的日常活动不受影响(并发执行)。
决定什么需要扔掉(标记结束)
虽然你在标记的过程中尽量不打扰到房间的正常使用,但最后你需要一个短暂的时间来确认一下是否所有需要的东西都被检查过了,确保没有遗漏(标记终止)。
开始打扫(清除阶段)
- 接下来,你开始清理那些没有标记的盒子,也就是你不再需要的东西(清除不活动的对象)。这个过程同样可以在你进行其它活动时一边进行(并发清除)。
- 你不需要一次性停下所有活动就开始清理,而是可以边做边清,减少影响。
一些小技巧(优化)
- 为了确保在清理过程中不会把新产生的需要的东西误判为垃圾,你使用了一种特别的记号(写屏障),这样即使在清理过程中也能正确处理新出现的东西。
- 你会根据房间使用情况和盒子的数量自动决定打扫的频率,如果房间很乱(内存分配快),就会增加打扫的次数,以保持房间的整洁(GC触发机制)。
通过这种方式,Go确保了程序中不再需要的内存可以被及时回收,从而让程序运行得更加高效,同时尽量减少了打扫工作对你的日常活动的影响。
总结
Go的垃圾回收(GC)实现主要依赖于并发的、标记-清除算法,以及一些优化技术来减少GC引起的延迟。下面是Go垃圾回收的基本工作流程和特性:
基本工作流程
-
标记阶段(Mark Phase):垃圾收集器遍历所有的根对象(比如全局变量和当前所有goroutine的栈上的变量),然后递归地标记所有从这些根对象可达的对象为活动的。这个过程是并发进行的,意味着它和应用程序的代码同时运行,减少了程序的暂停时间。
-
标记终止(Mark Termination):为了完成标记过程,需要一个短暂的停顿,确保所有的活动对象都被标记。这个阶段尽可能短,以减少对程序运行的影响。
-
清除阶段(Sweep Phase):在这一阶段,垃圾收集器会遍历堆上的所有对象,释放那些在标记阶段未被标记为活动的对象占用的内存。清除阶段通常也是并发进行的,不需要程序暂停。
特性和优化
-
写屏障(Write Barrier):Go使用写屏障技术,在标记阶段并发执行时,保证新生成的对象和对象引用更新的正确性。当程序修改一个对象的指针时,写屏障会确保这些变化不会破坏垃圾收集器的标记过程。
-
并发GC:Go的垃圾回收设计为并发执行,意味着GC的大部分工作是与程序的其它部分同时运行的,极大地减少了GC引起的停顿时间。
-
三色标记:在标记阶段,Go垃圾收集器使用"三色"标记算法,其中对象可以被标记为白色(未检查的)、灰色(已检查但其指向的对象未全部检查的)或黑色(已检查且其指向的对象也全部检查过的)。这有助于有效地管理和跟踪对象的活跃状态。
-
GC触发机制:Go的GC是自适应的,它根据堆的大小和程序的分配速率动态调整触发时机。这意味着如果程序分配内存的速度很快,GC就会更频繁地运行,以避免使用过多的内存。
Go的垃圾回收机制经过精心设计,以尽可能减少对程序性能的影响,同时保证高效地回收未使用的内存。这使得Go非常适合构建需要长时间运行和高性能的服务和应用程序。