当前位置: 首页 > 科技观察

Go语言是如何实现stoptheworld的?

时间:2023-03-13 17:41:09 科技观察

本文基于Go1.13。在一些垃圾收集器算法中,“停止世界”(StoptheWorld:STW,下同)是跟踪内存使用情况的最重要阶段。它停止程序的执行以扫描内存使用情况并添加写屏障。让我们回顾一下它内部是如何工作的,以及它可能面临的潜在问题。Stoptheworld停止一个程序意味着停止所有正在运行的goroutines。下面是一个执行STW的简单程序:funcmain(){runtime.GC()}运行垃圾收集器,它将触发STW的两个阶段。关于垃圾收集器周期的更多信息,推荐阅读我的另一篇文章《Go:垃圾收集器如何标记内存?①》第一步:抢占所有正在运行的goroutines:goroutinepreemption一旦goroutines被抢占,它们就会在安全点停止。同时,P处理器将被标记(正在运行的代码或在空闲列表中)停止以不运行任何代码:onthefreelist:Mhasbeenmovedtotheidlelist关于每个M上运行的goroutines,他们会在全局队列中等待:并在工作完成后启动整个世界。下面的跟踪图将有助于理解此阶段何时发生:跟踪“STW”阶段系统调用“STW”阶段也可能影响系统调用,因为它们可能会在STW时返回。让我们举一个密集执行的系统调用的例子,看看它是如何处理的:funcmain(){varwgsync.WaitGroupwg.Add(10)fori:=0;i<10;i++{gofunc(){http.Get(`https//httpstat.us/200`)wg.Done()}()}wg.Wait()}这是跟踪:STW阶段,系统调用正在结束。但是,由于没有Ps可用(如前一节所述,它们都被标记为已停止),goroutine将被放入一个全局队列中,并在世界恢复时稍后运行。延迟时间“STW”第三步涉及将所有M从它们的P中分离出来。然而,Go会等待它们自己停止:当调度程序正在运行时,在系统调用中等。等待goroutine被抢占应该很快,但在某些情况下,可能会导致一些延迟。我们以一个极端的情况为例:funcmain(){vartintfori:=0;i<20;i++{gofunc(){fori:=0;i<1000000000;i++{t++}}()}runtime.GC()在这里,“StoptheWorld”阶段需要2.6秒:没有函数调用的goroutines不会被抢占,直到任务结束才会释放P。这将迫使“STW”等待。有几种解决方案可以改善循环中的抢占,有关这方面的更多信息,我建议阅读我的另一篇文章“Go:Goroutines和抢占②”。相关链接:https://medium.com/a-journey-with-go/go-how-does-the-garbage-collector-mark-the-memory-72cfc12c6976https://medium.com/a-journey-with-go/go-goroutine-and-preemption-d6bc2aa2f4b7