Go 博客

Go 运行时:4 年后的回顾

Michael Knyszek
2022 年 9 月 26 日

自从我们2018 年关于 Go GC 的上一篇博文以来,Go GC 以及更广泛的 Go 运行时一直在稳步改进。我们处理了一些大型项目,这些项目由现实世界的 Go 程序和 Go 用户面临的实际挑战推动。让我们来回顾一下亮点吧!

有什么新功能?

这些更改对用户来说大多是不可见的:他们已经熟悉和喜爱的 Go 代码运行得更好,只需升级 Go 即可。

一个新的旋钮

Go 1.19 带来了一个长期以来一直被请求的功能,它需要一些额外的操作才能使用,但具有很大的潜力:Go 运行时的软内存限制

多年来,Go GC 只有一个调整参数:GOGCGOGC允许用户调整Go GC 做出的 CPU 开销和内存开销之间的权衡。多年来,这个“旋钮”很好地服务于 Go 社区,涵盖了各种用例。

Go 运行时团队一直不愿向 Go 运行时添加新的旋钮,这是有充分理由的:每个新的旋钮都代表配置空间中的一个新维度,我们需要对其进行测试和维护,可能永远如此。旋钮的激增也给 Go 开发人员带来了理解和有效使用它们的负担,随着旋钮数量的增加,这变得更加困难。因此,Go 运行时一直倾向于在最少的配置下表现合理。

那么为什么要添加内存限制旋钮呢?

内存不像 CPU 时间那样具有可替代性。对于 CPU 时间,如果你只是等待一段时间,将来总会有更多的时间。但是对于内存,你拥有的内存是有限的。

内存限制解决了两个问题。

第一个是,当应用程序的峰值内存使用不可预测时,仅使用GOGC几乎无法防止内存耗尽。仅使用GOGC,Go 运行时根本不知道它可以使用多少内存。设置内存限制使运行时能够通过使其意识到何时需要更努力地降低内存开销来增强对瞬态、可恢复的负载峰值的鲁棒性。

第二个是,为了避免内存不足错误而不使用内存限制,GOGC必须根据峰值内存进行调整,从而导致更高的 GC CPU 开销以维持较低的内存开销,即使应用程序没有处于峰值内存使用状态并且有足够的可用内存也是如此。这在我们的容器化世界中尤其相关,在容器化世界中,程序被放置在具有特定且隔离的内存预留的容器中;我们不妨充分利用它们!通过提供对负载峰值的保护,设置内存限制允许GOGC相对于 CPU 开销进行更积极的调整。

内存限制旨在易于采用且健壮。例如,它是应用程序 Go 部分的整个内存占用量的限制,而不仅仅是 Go 堆,因此用户不必担心计算 Go 运行时开销。运行时还会根据内存限制调整其内存清除策略,以便在响应内存压力时更积极地将内存返回到操作系统。

但是,虽然内存限制是一个强大的工具,但仍必须谨慎使用。一个主要的警告是它使你的程序容易受到 GC 抖动:程序花费太多时间运行 GC,导致没有足够的时间进行有意义的进展。例如,如果内存限制设置得太低,低于程序实际需要的内存,则 Go 程序可能会抖动。GC 抖动以前不太可能发生,除非GOGC明确地过度调整为有利于内存使用。我们选择优先考虑内存耗尽而不是抖动,因此作为缓解措施,运行时会将 GC 限制为总 CPU 时间的 50%,即使这意味着超过内存限制。

所有这些都需要仔细考虑,因此作为这项工作的一部分,我们发布了一个全新的 GC 指南,其中包含交互式可视化,以帮助您了解 GC 成本以及如何操作它们。

结论

试用内存限制!在生产环境中使用它!阅读GC 指南

我们一直在寻找关于如何改进 Go 的反馈,但听到它对您有效时也很有帮助。发送您的反馈

下一篇文章:Go 十三周年
上一篇文章:Go 开发者调查 2022 年第二季度结果
博客索引