Go 的垃圾回收器是基于三色标记清扫算法的,它在运行时进行并发垃圾回收,尽量减少对程序性能的影响。

自 Go 1.5 版本以来,垃圾回收器一直在不断优化。一些关键的特性包括:

并发垃圾回收:大部分的垃圾回收工作是在后台并发进行的,与应用程序的Goroutine同时运行,以减少停顿时间。 写屏障:用于在并发标记阶段保持程序状态的一致性。 可调整的垃圾回收目标:程序可以通过设置环境变量 GOGC 来控制垃圾回收的频率。默认情况下,当堆的大小增长到前一次垃圾回收后的两倍时,就会触发下一次垃圾回收。 从 Go 1.5 到 Go 1.18,Go 团队一直在努力减少垃圾回收对程序性能的影响,特别是在减少 STW(Stop-The-World)暂停时间方面。在最近的版本中,Go 还引入了一些改进,以进一步减少延迟和提高效率,比如:

改进的写屏障:在更高版本中,Go 改进了写屏障的实现,以减少其开销。 非均匀处理器访问(NUMA)感知的调度器:这有助于在多处理器系统中改善性能,尽管这与垃圾回收直接相关性较小。 内存分配器的优化:这包括对小对象分配器的优化和大对象的内存分配方式的改进。

Go 语言的垃圾回收器使用的是改进的三色标记清扫算法。这个算法的基本思想是将堆中的对象根据其可达性标记为不同的颜色,然后基于这些颜色来确定哪些对象应该被回收。下面是三色标记清扫算法的简化描述:

  • 初始标记(STW): 在垃圾回收开始时,执行一个短暂的停顿(Stop-The-World,STW),此时所有的Goroutines都会停止执行。 标记所有从根对象(如全局变量、栈上的变量)直接可达的对象为灰色。
  • 并发标记: 然后,垃圾回收器并发运行,不影响Goroutines的执行。 灰色对象会被检查它们的子对象,这些子对象如果是白色的(即未被访问的),则将它们标记为灰色,并继续扫描。 一旦灰色对象的所有子对象都被扫描过,该对象就会被标记为黑色,表示该对象及其所有子对象都已被访问。 这个过程会重复,直到没有更多的灰色对象为止。
  • 写屏障(Write Barrier): 在并发标记阶段,程序依然在运行并可能会修改对象图。为了跟踪这些变化,垃圾回收器使用了写屏障技术。 当程序写入一个对象的时候,写屏障会将任何指向白色对象的引用的黑色对象重新标记为灰色。
  • 最终标记(STW): 在并发标记完成后,执行第二次短暂的STW暂停。 处理剩余的灰色对象,确保所有存活的对象都已被标记。
  • 清扫(Sweep): 清扫阶段通常是并发执行的,不需要停止应用程序的执行。 在这个阶段,垃圾回收器会遍历堆,回收那些标记为白色的对象所占用的内存。 清扫完成后,垃圾回收周期结束。

Go 语言的垃圾回收机制具有以下优点和缺点:

  1. 优点
  • 自动内存管理: Go 的自动垃圾回收减轻了开发者管理内存的负担,避免了常见的内存管理错误,如内存泄漏和野指针。
  • 并发执行: 从 Go 1.5 开始,垃圾回收过程主要是并发进行的,这意味着它可以与用户的Goroutines同时运行,减少程序暂停时间。
  • 低延迟: Go 垃圾回收器旨在减少STW(Stop-The-World)暂停的时间,以便实现更低的延迟,这对于需要高响应速度的应用程序特别重要。
  • 可调节性: 开发者可以通过环境变量如 GOGC 来调整垃圾回收的触发时机,从而在性能和内存使用之间寻求平衡。
  • 内存安全: Go 的内存模型和垃圾回收机制共同提供了内存安全的保证,降低了安全漏洞的风险。
  1. 缺点
  • 不可预测的暂停时间: 尽管Go努力减少STW暂停时间,但垃圾回收引起的暂停在某些情况下依然可能影响性能,特别是在大规模或者低延迟的系统中。
  • 内存开销: 自动垃圾回收通常意味着比手动内存管理更高的内存开销。Go 在进行垃圾回收决策时会考虑到总堆大小,并可能保留更多内存以减少垃圾回收的频率。
  • CPU开销: 垃圾回收过程虽然是并发进行的,但它仍然会占用CPU资源,这可能对CPU密集型的应用程序造成影响。
  • 调优复杂性: 虽然Go提供了一些可调节垃圾回收行为的参数,但要优化这些参数以适应特定应用程序的需求可能需要深入理解垃圾回收器的工作原理和应用程序的内存使用模式。

垃圾回收器的非确定性: 自动垃圾回收的非确定性可能导致应用程序表现出不一致的性能特征,这在需要高度确定性的系统中可能是一个问题。