前言
作为一个 Gopher,想水这边文章已经好久了。
关于 Arena 的 API 我就不赘述了,没几个接口,翻一秒代码就了解完了:
本文就专注于分享一下我总结的 Arena 包的设计,前因后果,注意事项,以及未来的应用场景。
如有错误,欢迎评论区指正。
Arena 是怎么做到手动管理内存的?
如果要在 GC 的语言里面实现 C++ / Rust 那样的内存管理方式,会过于复杂,因为这要求 GC 算法必须很细心的兼容手动管理的内存段。
Arena 采用的方案是 Region-based memory management,具体流程如下:

- 开发者手动申请内存块。
- 开发者在这个内存块中不断申请新的内存空间,期间系统不进行回收。
- 开发者调用 API 释放整个内存块,然后系统进行内存回收。
受限于设计 Arena 无法适用于所有的场景,但已经能解决足够多的问题了。
如,对于常见的后端,可以通过如下手动管理内存的方式,流程优化性能:

- 当一个用户请求过来后,Http 框架会申请 Arena 内存空间。
- 处理请求期间程序所有产生的对象都会通过 Arena 创建。
- 在 Http 返回结束后,框架整块释放 Arena。
不会产生副作用的场景,都可以放心用 Arena。
为什么需要手动管理内存?
核心动力要追溯到 Golang 本身的应用场景。
Golang 常应用于一些性能敏感的服务如:云原生网关,RPC 框架,区块链,甚至很多数据库。
这些服务基本处于系统底层,一个性能的抖动会被放大数倍。
不巧,Golang 是自带 GC 的语言,GC 的不确定性对系统的稳定性始终是个隐患。
社区也没闲着,强壮的大佬们还是自食其力地去克服 GC 带来的困难,如 TIDB 就自己实现了一个 Arena:
总之,应用场景摆在这里,手动管理内存的能力引入是迟早的。
使用 Arena 要注意什么?
这一块内容主要就是对 Github Proposal 讨论内容的一个翻译和搬运了,有兴趣的可以看原 Proposal:
总之,使用 Arena 一定要注意它的生命周期。
例如在 Arena 中申请对象后,开发者要妥善处理这个对象,特别是需要将这个对象存储到缓存中时。
由于缓存的生命周期时常超越 Arena 的生命周期,一个不留神可能就导致,程序访问了一段已经被释放的内存。如下所示:

为了避免类似问题,需要程序员们小心翼翼地使用 Arena 包的 Clone API,在对象的生命周期大于 Arena 的生命周期时,把对象升级到 Heap 上。
小心,小心,再小心。
未来的应用场景
未来一定不是每一位 Gopher 都需要使用 Arena 的。
Arena 主要的应用场景可能如下:
- 在 ORM 框架,JSON 序列化框架,或者 RPC 序列化框架中引入。通过手动管理内存大幅提高性能。
据说 Google 内部已经应用了 Arena,并且获得了不错的性能收益。 - 云原生网关等底层组件进行性能升级,各大公司内部又是一波满满的 KPI 啊。
- 推出更多 Go 编写的中间件。如 Go 编写的 Reids 平替?可能性无限。
对于普通 Gopher 开发者,我认为大家最好的做法就是:不要在业务项目里面自己用 Arena。
大家只需要静等各个流行的类库进行一波升级后,直接坐地获得满满的性能收益。
这一波更新,大佬们获得了 KPI,我这种菜鸟坐吃收益,Gopher 们都赢麻了。