Go 语言如何实现垃圾回收中的 Stop the World (STW)

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

本篇文章讨论实现原理基于 Go 1.13.

在垃圾回收机制 (GC) 中,'Stop the World' (STW) 是一个重要阶段。顾名思义, 在 'Stop the World' 阶段, 当前运行的所有程序将被暂停, 扫描内存的 root 节点和添加写屏障 (write barrier) 。本篇文章讨论的是, 'Stop the World' 内部工作原理及我们可能会遇到的潜在风险。

Stop The World(STW)

这里面的'停止', 指的是停止正在运行的 goroutines。下面这段程序就执行了 'Stop the World':

func main() {   runtime.GC()}

这段代码中, 调用 runtime.GC() 执行垃圾回收, 会触发 'Stop the World'的三个步骤。

(关于关于垃圾回收机制, 可以参考我的另外一篇文章 ['Go:内存标记在垃圾回收中的实现'](https://medium.com/a-journey-with-go/go-how-does-the-garbage-collector-mark-the-memory-72cfc12c6976 ''Go:内存标记在垃圾回收中的实现'')):

这个阶段的第一步, 是抢占所有正在运行的 goroutine(即图中 G):

STW_goroutines_preemption

被抢占之后, 这些 Goroutine 会被悬停在一个相对安全的状态。同时,承载 Goroutine 的处理器 P (无论是正在运行代码的处理器还是已在 idle 列表中的处理器), 都会被被标记成停止状态 (stopped), 不再运行任何代码:

STW_P_stopped

接下来, Go 调度器 (Scheduler) 开始调度, 把每个处理器的 Marking Worker (即图中 M) 从各自对应的处理器 P 分离出来, 放到 idle 列表中去, 如下图:

STW_M_Detach

在停止了处理器和 Marking Worker 之后, 对于 Goroutine 本身, 他们会被放到一个全局队列中等待:

STW_G_Queue

到目前为止, 整个'世界'被停止. 至此, 仅存的 'Stop The World' (STW)goroutine 可以开始接下来的回收工作, 在一些列的操作结束之后, 再启动整个'世界'。

我们也可以在 Tracing 工具中看到一次 STW 的运行状态:

STW_TRACING

系统调用

下面我们来讨论一下 STW 是如何处理系统调用的。

我们知道, 系统调用是需要返回的, 那么当整个'世界'被停止的时候, 已经存在的系统调用如何被处理呢?

我们通过一个实际例子来理解:

func main() {   var wg sync.WaitGroup   wg.Add(10)   for i := 0; i < 10; i++ {      Go func() {         http.Get(`https://httpstat.us/200`)         wg.Done()      }()   }   wg.Wait()}

这是一段简单的系统调用的程序, 我们通过 Tracing 工具看一下它是如何被处理的:

SC_tracing

我们可以看到, 这个系统调用 Goroutine (即图中 G30) 在'世界'被停止的时候, 就已经存在了。

但是, 我们之前提到, STW 把所有的处理器 P 都标为停止状态 (stopped) , 所以, 这个系统调用的 Goroutine 也会被放到全局队列中, 等待 Golang 世界恢复之后, 被重新启用。

延迟

前文提到 STW 的第三步是将 Marking Worker(M) 从处理器(P)上分离, 然后放入 idle 列表中。

而实际上, Go 会等待他们自发停止, 也就是说当调度器(scheduler)运行的时候, 系统调用在运行的时候, STW 会等待。

理论上, 等待一个 Goroutine 被抢占是很快的, 但是在有些情况下, 还是会出现相应的延迟。

我们通过一个例子来模拟类似情况:

func main() {   var t int   for i := 0;i < 20 ;i++  {      Go func() {         for i := 0;i < 1000000000 ;i++ {            t++         }      }()   }   runtime.GC()}

我们还是来看一下这段代码运行的 Tracing 情况, 从下图我们可以看到 STW 阶段总共耗时 2.6 秒:

STW_26S

我们来简单分析为什么会出现这么长的 STW:正如例子中的 main 函数, 一个没有函数调用的 Goroutine 一般不会被抢占, 那么这个 Goroutine 对应的处理器 P 在任务结束之前不会被释放。

而 STW 的机制是等待它自发停止, 因此就出现了 2.6 秒的 STW。

为了提高整体程序的效率, 我们一般需要避免或者改进这种情况。

关于这部分, 大家可以参考我的另一篇文章代码会死循环吗?Go 异步抢占


via: https://medium.com/a-journey-with-go/go-how-does-go-stop-the-world-1ffab8bc8846

作者:Vincent Blanchon[1]译者:SarahChenBJ[2]校对:@unknwon[3]

本文由 GCTT[4] 原创编译,Go 中文网[5] 荣誉推出

参考资料

[1]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[2]

SarahChenBJ: https://github.com/SarahChenBJ

[3]

@unknwon: https://github.com/unknwon

[4]

GCTT: https://github.com/studygolang/GCTT

[5]

Go 中文网: https://studygolang.com/

(0)

相关推荐

  • 30 张图讲解:Golang调度器GMP原理与调度全分析

    本文作者:刘丹冰Aceld 公众号同名 该文章主要详细具体的介绍Goroutine调度器过程及原理,可以对Go调度器的详细调度过程有一个清晰的理解,花费4天时间作了30+张图(推荐收藏),包括如下几个 ...

  • 在Go中,你犯过这些错误吗

    Go语言中文网 今天 以下文章来源于吴亲强的深夜食堂 ,作者吴亲库里 吴亲强的深夜食堂关注一些奇奇怪怪的设计,分享一些有有趣趣的生活 迭代器变量上使用 goroutine 这算高频吧. package ...

  • recover.panic.defer.2021.03.03

    Defer, Panic, and Recover 在 Go 语言中,recover 和 panic 的关系是什么? 我们先看一个基础的例子,在 main 方法体中启动一个协程,在协程内部主动调用 p ...

  • 从 bug 中学:六大开源项目告诉你 go 并发编程的那些坑

    作者:richardyao,腾讯 CSIG 后台开发工程师 并发编程中,go不仅仅支持传统的通过共享内存的方式来通信,更推崇通过channel来传递消息,这种新的并发编程模型会出现不同于以往的bug. ...

  • 信道:如何通过信道完成Go程(goroutine)同步?

    中文译为信道,英文是Channel,发音为[ˈtʃænl]),在Go语言中简写为chan.chan是Go语言的基本数据类型之一,也是Go语言中为难不多三个使用make关键字进行初始化的三个类型(信道. ...

  • 22 Go常见的并发模式和并发模型

    一 Go并发模型 传统的编程语言C++ Java Python等,他们的并发逻辑多事基于操作系统的线程.并发执行单元(线程)之间的通信利用的就是操作系统提供的线程或进程间通信的原语.如:共享内存.信号 ...

  • 互斥锁与读写锁:如何使用锁完成Go程同步?

    图转自https://colobu.com/2018/12/18/dive-into-sync-mutex/这张图容易让人产生误解,容易让人误以为goroutine1获取的锁,只有goroutine1 ...

  • PHP中的垃圾回收相关函数

    PHP中的垃圾回收相关函数 之前我们已经学习过 PHP 中的引用计数以及垃圾回收机制的概念.这些内容非常偏理论,也是非常常见的面试内容.而今天介绍的则是具体的关于垃圾回收的一些功能函数.关于之前的两篇 ...

  • [PHP小课堂]PHP中的垃圾回收相关函数

    [PHP小课堂]PHP中的垃圾回收相关函数 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[xiaoyuezigonggong/149844827]免费得PHP.项目管理学习资料 知乎 ...

  • 说说Python中的垃圾回收机制?

    公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助! 小猿会从最基础的面试题开始, ...

  • 垃圾回收算法有几种类型? 他们对应的优缺点又是什么?

    常见的垃圾回收算法有: 标记-清除算法.复制算法.标记-整理算法.分代收集算法 标记-清除算法 标记-清除算法包括两个阶段:"标记"和"清除". 标记阶段:确定 ...

  • R语言计算资本资产定价模型(CAPM)中的Beta值和可视化

    原文链接:http://tecdat.cn/?p=22588 今天我们将计算投资组合收益的CAPM贝塔.这需要拟合一个线性模型,得到可视化,从资产收益的角度考虑我们的结果的意义. 简单的背景介绍,资本 ...

  • R语言使用马尔可夫链对营销中的渠道归因建模

    原文链接:http://tecdat.cn/?p=5383介绍在这篇文章中,我们看看什么是渠道归因,以及它如何与马尔可夫链的概念联系起来.我们还将通过一个电子商务公司的案例研究来理解这个概念如何在理论 ...

  • 技术贴 | R语言:geom_smooth在散点图中添加多条回归直线

    本文由阿童木根据实践经验而整理,希望对大家有帮助. 原创微文,欢迎转发转载. 导读 R语言lm函数可对两组数据进行回归分析.geom_point函数可以将数据绘制成散点图,geom_smooth函数可 ...

  • 时尚蜕变于传统 ——基于水墨语言在现代服装设计中的应用

    水之积也不厚,则其负大舟也无力. 风之积也不厚,则其负大翼也无力. -----<庄子·内篇·逍遥游> 关键词:水墨 传统 蜕变 时尚 Key words: Ink painting, tr ...

  • 面试题-python 垃圾回收机制?

    前言 简历上写着熟悉 python 面试官上来就问:说下python 垃圾回收机制?一盆冷水泼过来,瞬间感觉 python 不香了. Python中,主要通过引用计数(Reference Counti ...