Go 博客
Go 运行时:四年后
自 2018 年 关于 Go GC 的上一篇博文以来,Go GC 以及更广泛的 Go 运行时一直在稳步改进。我们承担了一些大型项目,这些项目源于真实的 Go 程序和 Go 用户面临的实际挑战。让我们来回顾一下亮点!
有什么新功能?
-
sync.Pool
,一个 GC 感知的内存重用工具,其延迟影响更低,并且比以前更有效地回收内存。(Go 1.13) -
Go 运行时将不需要的内存更积极地归还给操作系统,从而减少了过多的内存消耗和内存不足错误的发生几率。这使得闲置内存消耗减少了多达 20%。(Go 1.13 和 1.14)
-
在许多情况下,Go 运行时能够更及时地抢占 goroutine,将“停止世界”的延迟最多降低 90%。在此处观看 Gophercon 2020 的演讲。(Go 1.14)
-
Go 运行时比以前更有效地管理计时器,尤其是在拥有大量 CPU 核心的机器上。(Go 1.14)
-
使用
defer
语句延迟的函数调用在大多数情况下,其开销几乎与常规函数调用一样低。在此处观看 Gophercon 2020 的演讲。(Go 1.14) -
内存分配器的慢路径与 CPU 核心的结合度更好,扩展性更好,在高度并行的程序中,吞吐量最多提高 10%,尾部延迟最多降低 30%。(Go 1.14 和 1.15)
-
现在可以通过更细粒度、更灵活、更高效的 API,即 runtime/metrics 包来访问 Go 内存统计信息。这使得获取运行时统计信息的延迟降低了两个数量级(从毫秒降至微秒)。(Go 1.16)
-
Go 调度器花费在寻找新工作上的 CPU 时间最多减少 30%。(Go 1.17)
-
在 amd64、arm64 和 ppc64 上,Go 代码现在遵循基于寄存器的调用约定,CPU 效率最多提高 15%。(Go 1.17 和 Go 1.18)
-
Go GC 的内部计数和调度进行了重新设计,解决了与效率和健壮性相关的各种长期存在的问题。这使得在 goroutine 堆栈占内存很大一部分的应用程序中,应用程序尾部延迟显著降低(最多 66%)。(Go 1.18)
-
Go GC 现在限制了在应用程序空闲时自身的 CPU 使用量。这使得在非常空闲的应用程序中,GC 周期期间的 CPU 利用率降低了 75%,从而减少了可能导致作业调度器混淆的 CPU 峰值。(Go 1.19)
这些更改对用户来说大部分是不可见的:他们喜欢的 Go 代码通过升级 Go 版本就能运行得更好。
一个新配置项
Go 1.19 带来了一项长期请求的功能,该功能需要一些额外的操作才能使用,但具有巨大的潜力:Go 运行时的软内存限制。
多年来,Go GC 只有一个调整参数:GOGC
。GOGC
允许用户调整Go GC 造成的 CPU 开销和内存开销之间的权衡。多年来,这个“配置项”很好地服务了 Go 社区,涵盖了各种各样的用例。
Go 运行时团队一直不愿向 Go 运行时添加新的配置项,这并非没有原因:每一个新的配置项都代表着配置空间中一个新的维度,我们需要对其进行测试和维护,并且可能需要永远如此。此外,配置项的泛滥也给 Go 开发人员带来了理解和有效使用它们的负担,而配置项越多,这种负担就越重。因此,Go 运行时一直倾向于在最少的配置下也能表现良好。
那么,为什么要添加内存限制配置项呢?
内存不像 CPU 时间那样可以随意替换。CPU 时间,如果你稍等片刻,未来总会有更多的。但内存是有限的。
内存限制解决了两个问题。
首先,当应用程序的峰值内存使用不可预测时,仅靠 GOGC
几乎无法防止内存耗尽。仅使用 GOGC
,Go 运行时根本不知道它有多少可用的内存。设置内存限制可以使运行时能够应对短暂的、可恢复的负载峰值,因为它知道何时需要更努力地工作以减少内存开销。
其次,为了在不使用内存限制的情况下避免内存不足错误,必须根据峰值内存来调整 GOGC
,从而导致更高的 GC CPU 开销以维持较低的内存开销,即使在应用程序未达到峰值内存使用且有大量内存可用时也是如此。这在我们容器化的世界中尤其重要,程序被放置在具有特定和隔离内存限制的盒子中;我们应该充分利用它们!通过提供应对负载峰值的保护,设置内存限制可以使 GOGC
在 CPU 开销方面进行更积极的调整。
内存限制的设计易于采用且健壮。例如,它限制的是应用程序 Go 部分的整体内存占用,而不仅仅是 Go 堆,因此用户不必担心计算 Go 运行时的开销。运行时还会根据内存限制调整其内存回收策略,从而更积极地响应内存压力,将内存归还给操作系统。
但是,虽然内存限制是一个强大的工具,但仍需谨慎使用。一个主要的警告是,它可能会使你的程序面临 GC 抖动:即程序花费过多时间运行 GC,导致无法进行有意义的进展。例如,如果内存限制设置得太低,不足以满足程序实际需要的内存量,Go 程序可能会出现抖动。这在以前不太可能发生,除非 GOGC
被明确地大大倾向于内存使用。我们选择优先耗尽内存而不是出现抖动,因此作为一种缓解措施,运行时会将 GC 限制在总 CPU 时间的 50%,即使这意味着超过内存限制。
所有这些都需要仔细考虑,因此作为这项工作的一部分,我们发布了一个全新的 GC 指南,其中包含交互式可视化,帮助您理解 GC 的成本以及如何对其进行调整。
结论
试试内存限制!在生产环境中使用它!阅读GC 指南!
我们一直在寻求关于如何改进 Go 的反馈,同时也很乐意听到它对您来说就是好用的情况。给我们发送反馈!
下一篇文章:Go 的十三年
上一篇文章:2022 年第二季度 Go 开发者调查结果
博客索引