go1.19正式版在8月初上线了,现在可以在官方下载页使用1.19的特性。
一些比较有意义的改动:
内存模型
Go 的内存模型已被修订,以使 Go 与 C、C++、Java、JavaScript、Rust 和 Swift 使用的内存模型保持一致。
另外随着内存模型的更新,Go1.19 在 sync/atomic 包中引入了新的类型,使之更容易使用原子值,如 atomic.Int64 和 atomic.Pointer[T]。
并对文档做了以下具体的修改:
记录 Go 的整体内存模型描述。
记录 multiword 竞态会导致崩溃的情况。
记录 runtime.SetFinalizer 的 happens-before。
记录(或链接)更多同步类型的发生前。
记录同步/原子的发生时间,匹配 C++ 的顺序一致的原子(以及Java、JavaScript、Rust、Swift、C...)。
记录不允许的编译器优化。(这个只是 “修订”,是改了文档和定义,并不涉及内存模型的代码变更。)
支持龙芯架构
龙芯(Loongson)是由中国科学院计算技术研究所、龙芯中科、神州龙芯等机构、公司所设计的一系列各种芯片(包括通用中央处理器、SoC、微控制器、芯片组等)。
在 Go 1.19 起增加了对 Linux 上 Loongson 64 位架构的支持(GOOS=linux,GOARCH=loong64)。
竞态检测
Go 的竞态资源检测(race detector)已经发布到 v3 版本了,将会跟随 Go1.19 一起上线到生产可用。
与 v2 版相比,用到新版本的 race detector 在性能上快 1.5 倍到 2 倍,使用一半的内存,并且支持无限数量的 goroutine。
运行时-堆内存软限制
新版本的 Go 增加了 runtime.SetMemoryLimit 函数和 GOMEMLIMIT 环境变量。
关注到 runtime.SetMemoryLimit 函数为运行时提供了一个内存的软限制。
有了这个内存的软限制后,Go 运行时将会遵守这个内存限制,行为包括:调整垃圾回收的频率、更积极地将内存返回到底层系统等,来维持这个软内存的限制[1]。
另外即使 GOGC=off(或者是执行了 SetGCPercent(-1) 函数),也会遵守软内存的限制。
有了内存软限制,一般场景下,可以有效的防止由于堆内存分配过多,导致 Go 进程超出系统内存资源的最大被 KILL 的场景。
市面上现有的方案比如memory ballast[2]和auto gc tuner[3] 都是为了解决 Go 在没有充分利用内存的情况下,频繁触发 GC 导致 GC 占用 CPU 过高的优化手段。
memory ballast 通过在堆上分配一个巨大的对象(一般是几个 GB)来欺骗 GOGC,让 Go 能够尽量充分地利用堆空间来减少 GC 触发的频率。
uber 后来分享的 auto gc tuner 更智能一些,设定程序的内存占用阈值,通过 GC 期间对用户 finalizer 函数的回调来达成每次 GC 触发时都动态设置 GOGC,以使应用使用内存与目标逐渐趋近的目的。
上面两种方式说到底都是 hack,因为用户无法直接修改 GC 逻辑,所以只能对应用的行为进行修改以达到欺骗 gc pacer 最终避免 OOM 或优化 GC的目的。
这次新增加的 debug.SetMemoryLimit 从根本上解决了这个问题:
OOM 的场景:SetMemoryLimit 设置内存上限即可
GC 触发频率高的场景:SetMemoryLimit 设置内存上限,GOGC = off
怎么实现的呢?
简单来说,现在的 heap goal,也就是 Nn,是
-
用 memory limit 减去栈/以及其它非堆所占的空间
-
1+GOGC/100 * heap size + 栈大小 + 全局变量大小
两者中的较小值,如果用户设置了 memory limit,那就不会像以前一样只用 GOGC 去算 heap goal 而导致堆意外增长至 OOM。
该限制对于优化 Go 程序以在具有专用内存量的容器中尽可能高效地运行特别有用。但是不建议在有和其他程序共享内存的情况下故意关掉GOGC的情况下使用memory limit。
运行时-goroutine初始堆栈大小
新版本中 Go 运行时将根据 goroutine 的历史平均堆栈使用率来分配初始 goroutine 堆栈,可以有效避免一些不必要的堆栈增长和复制,在低于平均水平的情况下,能节省最多 2 倍的空间浪费。golang团队也对这种初始堆栈大小的影响做足了实验[4]。基于统计的初始 goroutine 栈对于那些海量连接的 api gateway 系统应该是个利好,以后应该不会再看到那些掐着函数去算栈大小的优化了。
[1]https://go.dev/doc/gc-guide#Memory_limit
[2]https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i-learnt-to-stop-worrying-and-love-the-heap
[3]https://eng.uber.com/how-we-saved-70k-cores-across-30-mission-critical-services/
[4]https://github.com/golang/go/commit/016d7552138077741a9c3fdadc73c0179f5d3ff7
文章评论