Go 博客

Go 运行时:四年后

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 程序可能会出现抖动。这在以前不太可能发生,除非 GOGC 被明确地大大倾向于内存使用。我们选择优先耗尽内存而不是出现抖动,因此作为一种缓解措施,运行时会将 GC 限制在总 CPU 时间的 50%,即使这意味着超过内存限制。

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

结论

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

我们一直在寻求关于如何改进 Go 的反馈,同时也很乐意听到它对您来说就是好用的情况。给我们发送反馈

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