Go 博客
Go 运行时:4 年后的回顾
自从我们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)
-
Go 内存统计信息现在可以通过更细粒度、更灵活且更高效的 API(runtime/metrics 软件包)访问。这将获取运行时统计信息的延迟降低了两个数量级(从毫秒到微秒)。(Go 1.16)
-
Go 调度程序花费的 CPU 时间减少了高达 30%,用于旋转以查找新的工作。(Go 1.17)
-
Go 代码现在遵循基于寄存器的调用约定在 amd64、arm64 和 ppc64 上,将 CPU 效率提高了高达 15%。(Go 1.17 和 Go 1.18)
-
Go GC 的内部会计和调度已重新设计,解决了与效率和鲁棒性相关的各种长期存在的问题。这导致应用程序尾部延迟显着降低(高达 66%),适用于 goroutine 堆栈占内存使用量很大一部分的应用程序。(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 程序可能会抖动。GC 抖动以前不太可能发生,除非GOGC
明确地过度调整为有利于内存使用。我们选择优先考虑内存耗尽而不是抖动,因此作为缓解措施,运行时会将 GC 限制为总 CPU 时间的 50%,即使这意味着超过内存限制。
所有这些都需要仔细考虑,因此作为这项工作的一部分,我们发布了一个全新的 GC 指南,其中包含交互式可视化,以帮助您了解 GC 成本以及如何操作它们。
结论
试用内存限制!在生产环境中使用它!阅读GC 指南!
我们一直在寻找关于如何改进 Go 的反馈,但听到它对您有效时也很有帮助。发送您的反馈!
下一篇文章:Go 十三周年
上一篇文章:Go 开发者调查 2022 年第二季度结果
博客索引