Go语言如何实现stop the world?

开发 后端
在某些垃圾回收器算法中,“停止世界”(Stop the World: STW,下同)是跟踪内存使用最重要的阶段,它会停止程序的执行,以扫描内存使用,并添加写障碍。让我们回顾一下它在内部如何工作,以及它可能面临的潜在问题。

 [[318932]]

本文基于 Go 1.13。

在某些垃圾回收器算法中,“停止世界”(Stop the World: STW,下同)是跟踪内存使用最重要的阶段,它会停止程序的执行,以扫描内存使用,并添加写障碍。让我们回顾一下它在内部如何工作,以及它可能面临的潜在问题。

停止世界(Stop the world)

停止程序意味着停止所有正在运行的 goroutine。下面是一个执行 STW 的简单程序:

 

  1. func main() { 
  2.    runtime.GC() 

运行垃圾回收器,将触发 STW 两个阶段。

有关垃圾回收器周期的更多信息,建议阅读我的另外一篇文章 “Go:垃圾收集器如何标记内存? ① ”

第一步:抢占所有正在运行的 goroutine:

 

 

 

 

goroutine 抢占

一旦 goroutine 被抢占,它们将在安全点停止。同时,P 处理器将(正在运行的代码或在空闲列表)被标记为已停止,以不运行任何代码:

 

 

 

 

P 标记为已停止

然后,Go 调度程序将运行,将每个 M 与其 P 各自分离,并将其放入空闲列表中:

 

 

 

 

M 已移至闲置清单

关于在每个上运行的 goroutine M,它们将在全局队列中等待:

 

 

 

 

Goroutine 在全局队列中等待

然后,一旦世界停止了,只有唯一活动的 goroutine 才能安全地运行,并在工作完成后启动整个世界。下面跟踪图将有助于理解此阶段发生在何时:

 

 

 

 

跟踪 “ STW”阶段

系统调用

“STW”阶段也可能会影响系统调用,因为它们可能会在 STW 时返回。让我们以一个密集执行系统调用的例子,并查看其如何处理:

 

  1. func main() { 
  2.    var wg sync.WaitGroup 
  3.    wg.Add(10) 
  4.    for i := 0; i < 10; i++ { 
  5.       go func() { 
  6.          http.Get(`https://httpstat.us/200`) 
  7.          wg.Done() 
  8.       }() 
  9.    } 
  10.    wg.Wait() 

这是跟踪:

 

 

 

 

STW 阶段,系统调用正在结束。但是,由于没有可用 P(如上一节所述,它们都被标记为已停止),goroutine 将被放入全局队列,并在世界恢复时稍后运行。

延迟时间

“STW” 第三步涉及将所有 M 与其 P 分离。但是,Go 将等待它们自行停止:在调度程序运行时,在 syscall 调用中等。等待 goroutine 被抢占应该很快,但是在某些情况下,可能会导致某些延迟。让我们以一个极端的情况为例:

 

  1. func main() { 
  2.    var t int 
  3.    for i := 0;i < 20 ;i++  { 
  4.       go func() { 
  5.          for i := 0;i < 1000000000 ;i++ { 
  6.             t++ 
  7.          } 
  8.       }() 
  9.    } 
  10.  
  11.    runtime.GC() 

在这里,“ Stop the World”阶段需要 2.6 秒:

 

 

 

 

没有函数调用的 goroutine 将不会被抢占,并且 P 在任务结束之前不会被释放。这将迫使“STW”等待。有几种解决方案可改善循环中的抢占,有关此方面的更多信息,建议阅读我另外一篇文章“ Go:Goroutine和抢占 ②”。

相关链接:

  1. https://medium.com/a-journey-with-go/go-how-does-the-garbage-collector-mark-the-memory-72cfc12c6976
  2. https://medium.com/a-journey-with-go/go-goroutine-and-preemption-d6bc2aa2f4b7

 

责任编辑:武晓燕 来源: 高可用架构
相关推荐

2021-08-10 07:27:42

Python引用计数法

2017-11-16 15:25:54

Go语言算法代码

2023-04-18 08:27:16

日志级别日志包

2022-11-10 07:43:45

2022-10-26 07:26:38

2015-09-16 17:30:20

安装Go语言Linux

2023-05-19 08:01:57

Go 语言map

2022-05-09 10:36:05

PythonPyScript开发者

2020-08-12 08:56:30

代码凯撒密码函数

2022-11-01 18:29:25

Go语言排序算法

2023-05-08 07:55:05

快速排序Go 语言

2024-02-06 17:57:06

Go语言任务

2019-01-02 13:11:53

GO语言缓存

2018-03-12 22:13:46

GO语言编程软件

2023-04-03 08:02:16

切片扩容GO

2015-12-21 14:56:12

Go语言Http网络协议

2022-05-19 14:14:26

go语言限流算法

2012-03-13 10:40:58

Google Go

2014-04-24 10:48:27

Go语言基础实现

2024-04-11 13:13:27

点赞
收藏

51CTO技术栈公众号