文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Go GC 怎么标记内存?颜色是什么含义?图解三色标记法

2024-12-11 19:29

关注

本文基于 Go 1.13。关于内存管理的概念的讨论在我的文章 Go 中的内存管理和分配[1] 中有详细的解释。

Go GC 的作用是回收不再使用的内存。实现的算法是并发的三色标记和清除回收法。本中文,我们研究三色标记法,以及各个颜色的不同用处。

你可以在 Ken Fox 的 解读垃圾回收算法[2] 中了解更多关于不同垃圾回收机制的信息。

标记阶段

这个阶段浏览内存来了解哪些块儿是在被我们的代码使用和哪些块儿应该被回收。

然而,因为 GC 和我们的 Go 程序并行,GC 扫描期间内存中某些对象的状态可能被改变,所以需要一个检测这种可能的变化的方法。为了解决这个潜在的问题,实现了 写屏障[3] 算法,GC 可以追踪到任何的指针修改。使写屏障生效的唯一条件是短暂终止程序,又名 “Stop the World”。

 

在进程启动时,Go 也在每个 processor 起了一个标记 worker 来辅助标记内存。

然后,当 root 被加入到处理队列中后,标记阶段就开始遍历和用颜色标记内存。

为了了解在标记阶段的每一步,我们来看一个简单的程序示例:

  1. type struct1 struct { 
  2.  a, b int64 
  3.  c, d float64 
  4.  e *struct2 
  5.  
  6. type struct2 struct { 
  7.  f, g int64 
  8.  h, i float64 
  9.  
  10. func main() { 
  11.  s1 := allocStruct1() 
  12.  s2 := allocStruct2() 
  13.  
  14.  func () { 
  15.   _ = allocStruct2() 
  16.  }() 
  17.  
  18.  runtime.GC() 
  19.  
  20.  fmt.Printf("s1 = %X, s2 = %X\n", &s1, &s2) 
  21.  
  22. //go:noinline 
  23. func allocStruct1() *struct1 { 
  24.  return &struct1{ 
  25.   e: allocStruct2(), 
  26.  } 
  27.  
  28. //go:noinline 
  29. func allocStruct2() *struct2 { 
  30.  return &struct2{} 

struct2 不包含指针,因此它被储存在一个专门存放不被其他对象引用的对象的 span 中。

 

不包含指针的结构体储存在专有的 span 中

这减少了 GC 的工作,因为标记内存时不需要扫描这个 span。

分配工作结束后,我们的程序强迫 GC 重复前面的步骤。下面是流程图:

 

扫描内存

GC 从栈开始,递归地顺着指针找指针指向的对象,遍历内存。扫描到被标记为 no scan 的 span 时,停止扫描。然而,这个工作是在多个协程中完成的,每个指针被加入到一个 work pool 中的队列。然后,后台运行的标记 worker 从这个 work pool 中拿到前面出列的 work,扫描这个对象然后把在这个对象里找到的指针加入到队列。

 


garbage collector work pool

颜色标记

worker 需要一种记录哪些内存需要扫描的方法。GC 使用一种 三色标记算法[4],工作流程如下:

这个初始步骤完成后,GC 会:

然后,GC 重复以上两步,直到没有对象可被标记。在这一时刻,对象非黑即白,没有灰色。白色的对象表示没有其他对象引用,可以被回收。

下面是前面例子的图示:

 

初始状态下,所有的对象被认为是白色的。然后,遍历到的且被其他对象引用的对象,被标记为灰色。如果一个对象在被标记为 no scan 的 span 中,因为它不需要被扫描,所以可以标记为黑色。

 

现在灰色的对象被加入到扫描队列并被标记为黑色:

 

对加入到扫描队列的所有对象重复做相同的操作,直到没有对象需要被处理:

 

处理结束时,黑色对象表示内存中在使用的对象,白色对象是要被回收的对象。我们可以看到,由于 struct2 的实例是在一个匿名函数中创建的且不再存在于栈上,因此它是白色的且可以被回收。

归功于每一个 span 中的名为 gcmarkBits 的 bitmap 属性,三色被原生地实现了,bitmap 对 scan 中相应的 bit 设为 1 来追踪 scan。

 

我们可以看到,黑色和灰色表示的意义相同。处理的不同之处在于,标记为灰色时是把对象加入到扫描队列,而标记为黑色时,不再扫描。

GC 最终 STW,清除每一次写屏障对 work pool 做的改变,继续后续的标记。

你可以在我的文章 Go GC 怎样监控你的应用[5] 中找到关于并发处理和 GC 的标记阶段更详细的描述。

runtime 分析器

Go 提供的工具使我们可以对每一步进行可视化,观察 GC 在我们的程序中的影响。开启 tracing 运行我们的代码,可以看到前面所有步骤的一个概览。下面是追踪结果:

 


traces of the garbage collector

标记 worker 的生命周期也可以在追踪结果中以协程等级可视化。下面是在启动之前先在后台等待标记内存的 Goroutine #33 的例子。

 


marking worker

 

来源:Go语言中文网内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯