当前位置: 首页 > 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.dtcms.com/a/311465.html

相关文章:

  • 数据资产是什么?
  • MySQL 内置函数
  • npm安装下载慢问题
  • 离线安装docker和docker-compose
  • 【人工智能agent】--服务器部署PaddleX 的 印章文本识别模型
  • JVM 调优中JVM的参数如何起到调优动作?具体案例,G1GC垃圾收集器参数调整建议
  • Junit5+Maven+RestAssured+Allure接口自动化框架
  • VScode对Ubuntu用root账号进行SSH远程连接开发
  • MSQL-聚簇索引与非聚簇索引的比较
  • k8s的pod的YAML问题
  • 公共卫生场景下漏检率↓76%:陌讯动态特征融合算法在口罩识别中的实战解析
  • 7月销售超5万辆,零跑汽车再创单月历史新高
  • 开源列式分布式数据库clickhouse
  • SpringBoot 启动富文本文字更改
  • .NET 中,Process.Responding 属性用于检查进程的用户界面是否正在响应
  • Linux性能监控与调优全攻略
  • SpringCloud微服务
  • 1分钟临时共享空间在线小工具实现
  • 存储成本深度优化:冷热分层与生命周期管理——从视频平台年省200万实践解析智能存储架构
  • Qt 实战教程:使用 QNetworkAccessManager 发送 HTTP POST
  • SM2国密算法的大数运算原理详解
  • (吃饭)质数时间
  • 防御保护综合案例
  • 二分查找:高效搜索有序数组
  • 【论文笔记】Multi-Behavior Graph Neural Networks for Recommender System
  • 深度学习loss总结(二)
  • [硬件电路-116]:模拟电路 - 信号处理电路 - 程控增益放大器
  • Batch Normalization(BN):深度学习中的“训练加速器”与实践指南
  • LLM Prompt与开源模型资源(3)如何写一个好的 Prompt
  • 【转】大模型安全治理的现状与展望