标签 垃圾回收 下的文章

Go 1.25中值得关注的几个变化

本文永久链接 – https://tonybai.com/2025/08/15/some-changes-in-go-1-25

大家好,我是Tony Bai。

北京时间2025年8月13日,Go 团队如期发布了 Go 语言的最新大版本——Go 1.25。按照惯例,每次 Go 大版本发布时,我都会撰写一篇“Go 1.x 中值得关注的几个变化”的文章。自 2014 年的 Go 1.4 版本起,这一系列文章已经伴随大家走过了十一个年头。

不过,随着我在版本冻结前推出的“Go 1.x 新特性前瞻”系列,以及对该大版本可能加入特性的一些独立的解读文章,本系列文章的形式也在不断演变。本文将不再对每个特性进行细致入微的分析,因为这些深度内容大多已在之前的《Go 1.25 新特性前瞻》一文中详细讨论过。本文将更聚焦于提炼核心亮点,并分享一些我的思考。

好了,言归正传,我们来看看Go 1.25带来了哪些惊喜!

语言变化:兼容性基石上的精雕细琢

正如 Go 一贯所做的,新版 Go 1.25 继续遵循 Go1 的兼容性规范。最令 Gopher 们安心的一点是:Go 1.25 没有引入任何影响现有 Go 程序的语言级变更

There are no languages changes that affect Go programs in Go 1.25.

这种对稳定性的极致追求,是 Go 成为生产环境首选语言之一的重要原因。

尽管语法层面波澜不惊,但语言规范内部却进行了一次“大扫除”——移除了“core types”的概念。这一变化虽然对日常编码无直接影响,但它简化了语言规范,为未来泛型可能的演进铺平了道路,体现了 Go 团队在设计层面的严谨与远见。关于此变化的深度解读,可以回顾我之前的文章《Go 1.25 规范大扫除:移除“Core Types”,为更灵活的泛型铺路》。

编译器与运行时:看不见的性能飞跃

如果说 Go 1.24 的运行时核心是优化 map,那么 Go 1.25 的灵魂则在于让 Go 程序更“懂”其运行环境,并对 GC 进行了大刀阔斧的革新。

容器感知型 GOMAXPROCS

这无疑是 Go 1.25 最具影响力的变化之一。在容器化部署已成事实标准的今天,Go 1.25 的运行时终于具备了 cgroup 感知能力。在 Linux 系统上,它会默认根据容器的 CPU limit 来设置 GOMAXPROCS,并能动态适应 limit 的变化。

这意味着,只需升级到 Go 1.25,你的 Go 应用在 K8s 等环境中的 CPU 资源使用将变得更加智能和高效,告别了过去因 GOMAXPROCS 默认值不当而导致的资源浪费或性能瓶颈。更多细节,请参阅我的文章《Go 1.25 新提案:GOMAXPROCS 默认值将迎 Cgroup 感知能力,终结容器性能噩梦?》。

实验性的 Green Tea GC

Go 1.25 迈出了 GC 优化的重要一步,引入了一个新的实验性垃圾收集器。通过设置 GOEXPERIMENT=greenteagc 即可在构建时启用。

A new garbage collector is now available as an experiment. This garbage collector’s design improves the performance of marking and scanning small objects through better locality and CPU scalability.

据官方透露,这个新 GC 有望为真实世界的程序带来 10%—40% 的 GC 开销降低。知名go开发者Josh Baker(@tidwall)在Go 1.25发布正式版后,在X上分享了自己使用go 1.25新gc(绿茶)后的结果,他开源的实时地理空间和地理围栏项目tile38的GC开销下降35%:

这是一个巨大的性能红利,尤其对于重度依赖GC的内存密集型应用。虽然它仍在实验阶段,但其展现的潜力已足够令人兴奋。对 Green Tea GC 设计原理感兴趣的朋友,可以阅读我的文章《Go 新垃圾回收器登场:Green Tea GC 如何通过内存感知显著降低 CPU 开销?》。

此外,Go 1.25 还修复了一个存在于 Go 1.21 至 1.24 版本中可能导致 nil pointer 检查被错误延迟的编译器 bug,并默认启用了 DWARFv5 调试信息,进一步缩小了二进制文件体积并加快了链接速度,对DWARFv5感兴趣的小伙伴儿可以重温一下我之前的《Go 1.25链接器提速、执行文件瘦身:DWARF 5调试信息格式升级终落地》一文,了解详情。

工具链:效率与可靠性的双重提升

强大的工具链是 Go 生产力的核心保障。Go 1.25 在此基础上继续添砖加瓦。

go.mod 新增 ignore 指令

对于大型 Monorepo 项目,go.mod 新增的 ignore 指令是一个福音。它允许你指定 Go 命令在匹配包模式时应忽略的目录,从而在不影响模块依赖的前提下,有效提升大型、混合语言仓库中的构建与扫描效率。关于此特性的详细用法,请见《Go 工具链进化:go.mod 新增 ignore 指令,破解混合项目构建难题》。

支持仓库子目录作为模块根路径

一个长期困扰 Monorepo 管理者和自定义 vanity import 用户的难题在 Go 1.25 中也得到了解决。Go 命令现在支持在解析 go-import meta 标签时,通过新增的 subdir 字段,将 Git 仓库中的子目录指定为模块的根。

这意味着,你可以轻松地将 github.com/my-org/my-repo/foo/bar 目录映射为模块路径 my.domain/bar,而无需复杂的代理或目录结构调整。这个看似微小但备受期待的改进,极大地提升了 Go 模块在复杂项目结构中的灵活性。想了解其来龙去脉和具体配置方法,可以参考我的文章《千呼万唤始出来?Go 1.25解决Git仓库子目录作为模块根路径难题》。

go doc -http:即开即用的本地文档

这是一个虽小但美的改进。新的 go doc -http 选项可以快速启动一个本地文档服务器,并在浏览器中直接打开指定对象的文档。对于习惯于离线工作的开发者来说,这极大地提升了查阅文档的便捷性。详细介绍见《重拾精髓:go doc -http 让离线包文档浏览更便捷》。

go vet 新增分析器

go vet 变得更加智能,新增了两个实用的分析器:

  • waitgroup:检查 sync.WaitGroup.Add 的调用位置是否错误(例如在 goroutine 内部调用)。
  • hostport:诊断不兼容 IPv6 的地址拼接方式 fmt.Sprintf(“%s:%d”, host, port),并建议使用 net.JoinHostPort。

这些静态检查能帮助我们在编码阶段就扼杀掉一批常见的并发和网络编程错误。

标准库:功能毕业与实验探索

标准库的演进是每个 Go 版本的重要看点。

testing/synctest 正式毕业

在 Go 1.24 中以实验特性登场的 testing/synctest 包,在 Go 1.25 中正式毕业,成为标准库的一员。它为并发代码测试提供了前所未有的利器,通过虚拟化时间和调度,让编写可靠、无 flakiness 的并发测试成为可能。我曾撰写过一个征服 Go 并发测试的微专栏,系统地介绍了该包的设计与实践,欢迎大家订阅学习。

encoding/json/v2 开启实验

这是 Go 1.25 最受关注的实验性特性之一!通过 GOEXPERIMENT=jsonv2 环境变量,我们可以启用一个全新的、高性能的 JSON 实现。

Go 1.25 includes a new, experimental JSON implementation… The new implementation performs substantially better than the existing one under many scenarios.

根据官方说明,json/v2 在解码性能上相较于 v1 有了“巨大”的提升。这是 Go 社区多年来对 encoding/json 包性能诟病的一次正面回应。虽然其 API 仍在演进中,但它预示着 Go 的 JSON 处理能力未来将达到新的高度。对 v2 的初探,可以参考我的文章《手把手带你玩转 GOEXPERIMENT=jsonv2:Go 下一代 JSON 库初探》。jsonv2支持真流式编解码的方法,也可以参考《Go json/v2实战:告别内存爆炸,掌握真流式Marshal和Unmarshal》这篇文章。

sync.WaitGroup.Go:并发模式更便捷

Go 语言的并发编程哲学之一就是让事情保持简单。Go 1.25 在 sync.WaitGroup 上新增的 Go 方法,正是这一哲学的体现。

这个新方法旨在消除 wg.Add(1) 和 defer wg.Done() 这一对经典的样板代码。现在,你可以直接调用 wg.Go(func() { … }) 来启动一个被 WaitGroup 追踪的 goroutine,Add 和 Done 的调用由 Go 方法在内部自动处理。这不仅让代码更简洁,也从根本上避免了因忘记调用 Add 或 Done 而导致的常见并发错误。

关于这个便捷方法的来龙去脉和设计思考,可以回顾我之前的文章《WaitGroup.Go 要来了?Go 官方提案或让你告别 Add 和 Done 样板代码》。

其他:Trace Flight Recorder

最后,我想特别提一下 runtime/trace 包新增的 Flight Recorder API。传统的运行时 trace 功能强大但开销巨大,不适合在生产环境中持续开启。

trace.FlightRecorder 提供了一种轻量级的解决方案:它将 trace 数据持续记录到一个内存中的环形缓冲区。当程序中发生某个重要事件(如一次罕见的错误)时,我们可以调用 FlightRecorder.WriteTo 将最近一段时间的 trace 数据快照保存到文件。这种“事后捕获”的模式,使得在生产环境中调试偶发、疑难的性能或调度问题成为可能,是 Go 诊断能力的一次重大升级。更多详情可以参阅《Go pprof 迎来重大革新:v2 提案详解,告别默认注册,拥抱飞行记录器》。

小结

Go 1.25 的发布,再次彰显了 Go 语言务实求进的核心哲学。它没有追求华而不实的语法糖,而是将精力聚焦于那些能为广大开发者带来“无形收益”的领域:更智能的运行时、更快的 GC、更可靠的编译器、更高效的工具链

这些看似底层的改进,正是 Go 作为一门“生产力语言”的价值所在。它让开发者可以专注于业务逻辑,而将复杂的系统优化和环境适配,放心地交给 Go 语言自身。

我鼓励大家尽快将 Go 1.25 应用到自己的项目中,亲自感受这些变化带来的提升。Go 的旅程,仍在继续,让我们共同期待它在未来创造更多的可能。

感谢阅读!

如果这篇文章让你对 Go 1.25 新特性有了新的认识,请帮忙 点赞分享,让更多朋友一起学习和进步!


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

Go vs. Rust再掀波澜:Grab真实案例复盘,Gopher如何看待这场“效率与代价”之争?

本文永久链接 – https://tonybai.com/2025/06/24/grab-rewrote-go-service-in-rust

大家好,我是Tony Bai。

最近,东南亚科技巨头、出行公司 Grab 的一篇技术博客《Counter Service: How we rewrote it in Rust》在技术圈引起了不小的震动。他们将一个高 QPS(每秒查询率)的 Go 微服务(Counter Service)用 Rust 进行了重写,结果令人瞩目:在保持相似 P99 延迟性能的前提下,基础设施成本降低了高达 70%!


P99延迟对比:Go(紫色),Rust(蓝色)

这个案例无疑给许多以 Go 作为主力语言的团队和开发者带来了强烈的冲击。Go 语言以其简洁、高效并发、快速编译以及强大的生态系统,在微服务、云原生领域早已占据重要地位。那么,Grab 的这次成功“叛逃”,是否意味着 Go 语言在某些场景下的“护城河”正在被侵蚀?Rust 真的是解决一切性能和成本问题的“银弹”吗?

今天,我们就来深入剖析 Grab 的这个重构案例,看看他们究竟“得”了什么,“失”了什么,以及这背后能给咱们 Gopher 带来哪些宝贵的启示。

Rust 的“杀手锏”:极致效率带来的基础设施成本骤降

Grab 的 Counter Service 主要负责计数和提供 ML 模型/欺诈规则的计数器服务,是一个典型的 I/O 密集型和计算密集型并存的服务,QPS 峰值可达数万。用 Go 实现时,该服务需要大约 20 个 CPU Cores 来支撑。

然而,在用 Rust 重写后,同样的负载下,新的 Rust 服务仅需 4.5 个 CPU Cores!这几乎是 近80% 的资源节省,直接带来了 70%以上的基础设施成本降低。

为什么 Rust 能做到如此极致的效率提升?Grab 的文章和 Rust 语言本身的特性共同揭示了答案:

  • 无垃圾回收 (GC):这是 Rust 相比 Go 在追求极致性能和资源控制上的核心优势。Go 的 GC 虽然已经非常优秀,但在高并发、低延迟场景下,GC 扫描和 STW (Stop-The-World) 仍然可能引入不可预测的延迟抖动和额外的 CPU 开销。Rust 通过所有权系统在编译期保证内存安全,无需运行时 GC,从而消除了这部分开销。
  • 内存安全与零成本抽象:Rust 的所有权、借用检查等机制虽然带来了陡峭的学习曲线,但也确保了内存安全,避免了空指针、数据竞争等常见问题。同时,Rust 的许多高级抽象(如迭代器、闭包)能够在编译期被优化掉,实现“零成本抽象”,性能接近 C/C++。
  • 更精细的控制:Rust 赋予开发者对内存布局、线程模型更细致的控制权,使得在特定场景下可以进行深度优化。

Grab 的案例似乎在证明,当业务场景对资源消耗和运行成本极度敏感,且服务逻辑相对“简单”(Grab 特别强调了选择重写目标时,功能需要足够简单,复杂度可控)时,Rust 的这些特性能够带来实实在在的巨大回报。

光鲜背后的“代价”:Grab 的探索与挑战

然而,享受 Rust 带来的极致效率并非没有代价。Grab 团队在博客中也坦诚地分享了他们遇到的挑战和权衡:

  • 陡峭的学习曲线,尤其是 async

文章提到:对于习惯了 Go 语言简洁 go关键字和 GMP 调度模型的 Gopher 来说,Rust 的所有权、生命周期已经是第一道坎,而 async/await 异步模型及其“函数着色”问题、显式 yield(通过 await)等概念,则带来了更高的认知负荷。Grab 团队也曾因错误地在异步代码中使用了同步 Redis 调用而导致性能不佳。

  • 生态系统与内部库的“阵痛”

虽然 Rust 的生态在快速发展,但在某些特定领域,库的选择可能不如 Go 那样成熟和丰富。Grab 团队在选择 Datadog 和 Redis 客户端库时就进行了一番评估和取舍。

更痛的是内部库的迁移。Grab 内部大量基础库是用 Go 编写的,例如一个使用 Go Templates 进行配置管理的库。在 Rust 项目中,这些 Go 库无法直接复用,团队不得不使用 nom 解析器组合库在 Rust 中重写了类似的功能。这无疑增加了重构的成本和时间。

  • 开发体验的差异

Go 的设计哲学之一就是“简单”,这使得开发者能够快速上手并高效迭代。Goroutine 和 Channel 的易用性,让并发编程的门槛大大降低。相比之下,Rust 为了安全和性能,在语言层面引入了更多复杂性,需要开发者投入更多精力去理解和驾驭。

  • “Rust 一定比 Go 快得多”是迷思

一个非常重要的发现是,Grab 明确指出:“神话 1:Rust 非常快!比 Golang 更快!判定:被驳斥。Golang 对于大多数使用案例来说“足够快”……仅仅为了性能提升而将 Golang 服务重写为 Rust 不太可能带来显著的好处。”

在 P99 延迟方面,Rust 版本与 Go 版本表现相当,甚至有时略差。这告诉我们,Go 在其设计领域内性能已经足够优秀,单纯为了追求“更极致”的速度而用 Rust 重写 Go 服务,可能并不能带来预期的巨大性能提升,反而可能因为生态、开发效率等问题得不偿失。Grab 的主要收益点在于显著的 资源效率 提升。

Gopher 何去何从?几点思考

Grab 的案例无疑是 Go 社区的一面镜子,它照见了 Go 的优势,也揭示了在特定场景下可能存在的“天花板”。作为 Gopher,我们应如何看待这个案例,并从中吸取经验呢?

首先,Go 的核心优势依然稳固。Go 语言以其简洁性、强大的并发模型(Goroutine + Channel)、高效的编译速度、完善的工具链以及成熟的生态系统,继续在云原生、微服务、中间件和 DevOps 工具等领域占据首选或极具竞争力的地位。对于绝大多数业务场景,Go 提供的开发效率和运行性能是“足够好”的,且具有高性价比。

其次,关于何时考虑使用 Rust 进行“动刀”,Grab 的案例提供了几个关键的决策参考点。

  • 在面对极高的 QPS 和资源消耗时,如果服务本身成为性能瓶颈且占用了大量服务器资源,那么迁移可能是合适的。
  • 当功能相对简单且逻辑内聚时,重写的复杂度较低,易于验证,这样可以避免对复杂业务系统进行大规模重写。
  • 当基础设施成本成为显著负担,优化能带来巨大的商业价值时,也应考虑使用 Rust。
  • 团队必须具备掌握 Rust 的能力,成员需熟悉 Rust 并愿意投入时间和资源进行团队赋能。在不满足这些前提条件的情况下,盲目追求 Rust 可能弊大于利。

再者,在考虑语言迁移之前,我们应充分挖掘 Go 本身的优化潜力。例如,进行代码层面的性能分析与优化、架构调整、选择更优的 Go 库,甚至是通过 Go 版本升级带来的 GC 改进等。重写通常应视为最后的手段。

关于 Gopher 是否需要拥抱 Rust,这取决于个人的发展方向和兴趣。如果你专注于业务开发和应用层构建,Go 依然能让你游刃有余。但如果你对系统编程、底层优化、嵌入式或游戏引擎等领域感兴趣,或者所在的公司/团队正在引入 Rust,那么学习 Rust 无疑会为你打开一扇新的大门。即使不深入学习,了解 Rust 的核心理念(如所有权、生命周期和无GC)也能帮助我们更好地理解程序运行的本质,从而写出更健壮、更高效的 Go 代码。

最后,Go 语言的未来同样值得关注。Go 社区在持续进化,例如对泛型的支持提升了表达力,而持续优化的 GC 以及不断丰富的高性能标准库也在不断减少对性能的影响。未来,Go 是否会在某些方面借鉴其他语言的优秀特性,以保持其核心优势的同时,进一步拓展能力边界,值得我们期待。

小结

Grab 用 Rust 重写 Go 服务的案例,再次印证了技术选型中“没有银弹,只有取舍”的黄金法则。Rust 以其极致的性能和资源控制能力,在特定场景下展现了巨大的潜力。但这并不意味着 Go 已经过时或不再优秀。

对于我们 Gopher 而言,重要的是理解不同语言的设计哲学、优势与代价,并根据具体的业务场景、团队能力和长远目标,做出最适合的决策。

你对 Grab 的这个案例有什么看法?你认为在哪些场景下,用 Rust 替代 Go 是值得考虑的?欢迎在评论区留下你的思考!


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com
这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats