写点什么

go 怎样做 stw

用户头像
hxzhouh
关注
发布于: 2020 年 05 月 08 日
go 怎样做 stw



原文链接: Go: How Does Go Stop the World?:

Author : Vincent Blanchon

本文基于 go 1.13



在垃圾回收算法中,Stop The Word(STW)是一个很重要的概念,他会中断程序运行,添加写屏障,以便扫描内存 ,现在一起来看看它内部的原理以及可能存在的问题.

STW



停止程序运行意味着停止所有运行态的goroutines,一个简单的例子:

func main() {
runtime.GC()
}



运行垃圾回收算法将触发两个阶段的STW

有关垃圾回收的更多细节,请参考同作者的另外一篇文章Go: How Does the Garbage Collector Mark the Memory?



STW的步骤

第一步,抢占所有正在运行的goroutines



第二步,一旦 goroutines被抢占,正在运行的goroutines将在安全的地方暂停,然后所有的p[1]都将被标记为暂停,停止运行任何代码。





第三步,然后,go调度器将M[2]与P分离,并且将M放到空闲列表里面。





对于在每个M上运行的Goroutines,它们将在全局队列[3]>中等待:



那么,一旦所有的goroutines都停止了,那么唯一活跃的goroutines (垃圾回收goroutines)将会安全的运行,并且在垃圾回收完成后,重新拉起所有的goroutines。具体情况,可以通过 go trace查看。



System calls

STW时期可能会影响系统调用,因为系统调用可能会在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()
}



他的trace情况。



系统调用在STW时期返回,但是现在已经没有P在运行了。所以他会放到全局队列里面,等待STW结束后再运行。

Latencies

STW的第三步将所有的M与P分离。然而,go将等待调度程序运行、系统调用等自动停止。等待goroutine被抢占应该很快,但是在某些情况下会产生一些延迟,下面是一个极端的例子:

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

STW时长达到了2.6S



没有函数调用的goroutine将不会被抢占,并且它的P在任务结束之前不会被释放。这将迫使STW等待他, 有几种解决方案可改善循环中的抢占,有关其的更多信息,可以查看作者另外一篇文章 [Go: Goroutine and Preemption).



用户头像

hxzhouh

关注

种一棵树,最好的时间是十年前,其次是现在 2018.03.09 加入

还未添加个人简介

评论

发布
暂无评论
go 怎样做 stw