【Golang详解】go语言垃圾回收GC(二) 实现原理

实现原理

垃圾收集的多个阶段:

清理终止阶段(STW)
  • 暂停程序,所有的处理器在这时会进入安全点

我的理解是这里stw,等待所有协程都知道要开始打开写屏障了,不然无法做到统一

  • 如果当前垃圾收集循环是强制触发的,我们还需要处理还未被清理的内存管理单元
标记阶段-并发执行
  • 将状态切换至 _GCmark

  • 开启写屏障

  • 并发标记

  • 标记辅助(下面介绍)

  • 在这期间遵守混合写屏障的机制

标记终止阶段 -STW
  • 暂停程序、将状态切换至 _GCmarktermination 并关闭辅助标记的用户程序;通知其他goroutine关闭写屏障,停止写屏障
  • 清理处理器上的线程缓存
清理阶段-并发执行
  • 将状态切换至 _GCoff 开始清理阶段,初始化清理状态并关闭写屏障;
  • 恢复用户程序,这时候新创建的对象会标记为白色
  • 后台并发清理所有的内存管理单元

触发时机

主动触发

  • runtime.GC — 用户程序手动触发垃圾收集;

被动触发

  • 默认2min没有触发过GC,则触发一次GC

  • 分配内存时,若内存分配达到一定比例则触发

GC对CPU的使用率

在GC准备阶段,go语言给每个P创建一个mark worker协程(就是标记的协程),把对应的g指针存储到p中,这些mark worker创建后很快陷入休眠,等到标记阶段得到调度执行

GC默认的CPU目标使用率为25%

GC在mark worker中引入了三种不同的工作模式:

gcMarkWorkerFractionalMode -- 当垃圾收集的后台 CPU 使用率达不到预期时(默认为 25%),启动该类型的工作协程帮助垃圾收集达到利用率的目标,因为它只占用同一个 CPU 的部分资源,所以可以被调度;

gcMarkWorkerDedicatedMode -- 处理器专门负责标记对象,不会被调度器抢占

gcMarkWorkerIdleMode -- 当处理器没有可以执行的 Goroutine 时,它会运行垃圾收集的标记任务直到被抢占;

三种不同模式的工作协程会相互协同保证垃圾收集的 CPU 利用率达到期望的阈值

标记辅助

用户程序有可能在GC期间分配新的内存,为了保证用户程序分配内存的速度不会超出后台任务的标记速度,运行引入了标记辅助技术。

在目前Go实现中,当GC触发后,首先进入并发标记阶段,并发标记会设置一个标志gcAssistBytes 字段,这个字段存储了当前协程辅助标记的对象字节数。在并发标记阶段期间,当 Goroutine 分配新对象时,会检查此时 是否处于入不敷出的状态。

如果是,会暂停分配内存过快的哪些goroutine,并将其转去执行一些辅助标记的工作,从而达到放缓继续分配、辅助GC的标记工作的目的

参考资料

Go GC 篇_哔哩哔哩_bilibili

【Golang】粗线条话GC(二)_哔哩哔哩_bilibili

Go 语言垃圾收集器的实现原理 | Go 语言设计与实现 (draveness.me)

posted @ 2021-10-26 15:29  CJ-cooper  阅读(231)  评论(0编辑  收藏  举报