当前位置: 首页 > news >正文

深入 Go 底层原理(六):垃圾回收(GC)

1. 引言

Go 语言自带垃圾回收(Garbage Collection, GC),让开发者从手动管理内存的繁重任务中解脱出来。Go 的 GC 以其低延迟并发性而闻名,其目标是在不长时间暂停(Stop The World, STW)整个程序的情况下完成大部分回收工作。

本文将深入探讨 Go GC 的核心算法——并发三色标记清除法,以及其关键技术——写屏障(Write Barrier)。

2. GC 的目标与挑战
  • 目标:回收堆上不再被使用的对象(垃圾),并将内存返还给分配器。

  • 挑战:如何在不影响程序正常运行(即低 STW 时间)的前提下,准确、高效地完成回收?这需要在“mutator”(用户 Goroutine,会修改对象引用关系)和“collector”(GC Goroutine)之间进行精妙的协调。

3. 核心算法:并发三色标记-清除 (Tri-color Mark-and-Sweep)

Go GC 采用的是三色标记清除算法,它将堆上的对象分为三种颜色:

  • 白色 (White):对象的初始状态,代表可能是垃圾。在 GC 周期结束时,所有仍然是白色的对象都将被回收。

  • 灰色 (Grey):对象本身已被标记为存活,但其引用的其他对象(其子对象)还没有被扫描。灰色对象是待处理任务的集合。

  • 黑色 (Black):对象本身和其引用的所有子对象都已被扫描,是确认的存活对象。

GC 流程分为四个主要阶段

  1. Mark Setup (STW):

    • 这是一个短暂的 STW 阶段(通常在微秒级别)。

    • 主要任务是开启写屏障 (Write Barrier),并准备标记工作。

    • 将所有全局变量和每个 Goroutine 栈上的对象(根对象)放入灰色集合。

  2. Marking (Concurrent):

    • 这是 GC 的主要工作阶段,与用户 Goroutine 并发执行

    • GC Goroutine 会从灰色集合中取出一个对象,将其标记为黑色

    • 然后扫描该对象的所有指针字段,将其引用的所有白色对象标记为灰色,并放入灰色集合。

    • 这个过程会一直持续,直到灰色集合为空。

  3. Mark Termination (STW):

    • 这是另一个短暂的 STW 阶段。

    • 主要任务是处理一些在并发标记阶段中被写屏障捕获的、可能被遗漏的指针修改,并关闭写屏障。

  4. Sweeping (Concurrent):

    • 此阶段也与用户 Goroutine 并发执行

    • GC 会遍历堆中的所有 mspan,回收所有仍然是白色对象的内存块,并将其返还给内存分配器。

4. 关键技术:写屏障 (Write Barrier)

在并发标记阶段,如果用户 Goroutine(mutator)修改了对象的引用关系,可能会破坏三色标记的不变性,导致本应存活的对象被错误回收。

危险场景:一个黑色对象引用了一个白色对象,同时该白色对象的所有其他灰色父引用被移除了。如果不加干预,这个白色对象将永远不会被扫描,最终被当成垃圾回收。

黑色对象 -> 白色对象

为了防止这种情况,Go 引入了写屏障。写屏障是编译器插入的一小段代码,它会“拦截”所有在堆上的指针写操作。

混合写屏障 (Hybrid Write Barrier, Go 1.8+): Go 的混合写屏障结合了两种屏障的优点,其核心思想是:

  • 它保护的是白色对象:不允许黑色对象直接引用白色对象。

  • 工作机制:当 *slot = ptr(一个指针写操作)发生时,如果 ptr 指向一个白色对象,写屏障会ptr 指向的对象涂成灰色

通过这种方式,任何可能被黑色对象引用的白色对象都会被“拯救”回来,加入灰色集合,从而保证了 GC 的正确性。

5. GC 的触发时机

GC 主要由以下条件触发:

  1. 内存分配阈值 (GOGC): 当自上次 GC 以来新分配的内存达到一个阈值时,会自动触发新的 GC。这个阈值由环境变量 GOGC 控制(默认为 100),表示当堆大小增长 100% 时触发。

  2. 定时触发: runtime.sysmon 线程会定期检查,如果距离上次 GC 超过一定时间(默认为 2 分钟),会强制触发一次 GC。

  3. 手动触发: 开发者可以调用 runtime.GC() 来手动触发一次 GC。

6. 总结

Go GC 是一个低延迟、高并发的垃圾回收系统。它通过三色标记清除算法实现了大部分工作的并发执行,通过短暂的 STW 完成必要的同步,并通过混合写屏障技术保证了在用户 Goroutine 并发修改对象引用时的正确性。这一系列精巧的设计,是 Go 能够胜任高并发、低延迟服务场景的重要保障。

http://www.xdnf.cn/news/1232623.html

相关文章:

  • Kafka——关于Kafka动态配置
  • 洛谷 P3870 [TJOI2009] 开关-普及+/提高
  • 【学习笔记】MySQL技术内幕InnoDB存储引擎——第7章 事务
  • 【Java】在一个前台界面中动态展示多个数据表的字段及数据
  • InfluxDB 与 Node.js 框架:Express 集成方案(二)
  • 中州养老项目:Mybatis自动填充拦截器
  • 大模型Agent记忆的主流技术与优缺点解析
  • 网页操作自动化解决方案:如何用Browser-Use+CPolar提升企业运营效率
  • CYUSB3014-BZXC-USB3.0接口芯片-富利威
  • 解锁智能油脂润滑系统:加速度与温振传感器选型协同攻略
  • Javascript面试题及详细答案150道之(016-030)
  • 前端与后端部署大冒险:Java、Go、C++三剑客
  • SQL语言学习(group by,having)
  • 半导体物理复习
  • TypeScript03-web项目知识
  • 路面障碍物识别漏检率↓76%:陌讯多模态融合算法实战解析
  • linux 启动流程?
  • C++入门基础(三):const引用、指针和引用的关系、inline(修饰内联函数)替代宏、nullptr代替null
  • .env 文件
  • 对于考研数学的理解
  • 【MySQL】增删改查操作 —— CRUD
  • 【软考中级网络工程师】知识点之级联
  • 驾驶场景玩手机识别:陌讯行为特征融合算法误检率↓76% 实战解析
  • 数字化转型驱动中小制造企业的质量管理升级
  • 梦幻花瓣雨
  • [mssql] 分析SQL Server中执行效率较低的SQL语句
  • 关于echarts的性能优化考虑
  • 蓝桥杯----串口
  • 我的世界进阶模组教程——物品(2)
  • 【redis】基于工业界技术分享的内容总结