本提案所提到的 Arena,指的是一种从一个连续的内存区域分配一组内存对象的方式。优点是 arena 中的对象分配通常比一般的内存分配更有效率,所分配的对象可以一次性释放,以此达到内存管理或垃圾收集的开销最小。
背景
由于手动管理内存普遍会给程序员带来一定的心智负担,提高一门编程语言的入门门槛(还记得大学写 OC 时经常有同学写着写崩了...)。
对应到 Go 语言上,他是一门带垃圾回收的编程语言。也就是说不需要程序员手动的去管理、释放程序的内存。
无需手动管理也是 Go 核心开发团队一直引以为傲的特性之一。
最近有人发起了一个新提案《proposal: arena: new package providing memory arenas》,引起了非常广泛的讨论。
如下图:
接下来我们将面向该提案进行学习和了解。
新提案
本提案所提到的 Arena,指的是一种从一个连续的内存区域分配一组内存对象的方式。优点是 arena 中的对象分配通常比一般的内存分配更有效率,所分配的对象可以一次性释放,以此达到内存管理或垃圾收集的开销最小。
其建议在 Go 的标准库中支持 arena。标准 API 如下:
package arena
type Arena struct {
// contains filtered or unexported fields
}
// New allocates a new arena.
func New() *Arena
// Free frees the arena (and all objects allocated from the arena) so that
// memory backing the arena can be reused fairly quickly without garbage
// collection overhead. Applications must not call any method on this
// arena after it has been freed.
func (a *Arena) Free()
// New allocates an object from arena a. If the concrete type of objPtr is
// a pointer to a pointer to type T (**T), New allocates an object of type
// T and stores a pointer to the object in *objPtr. The object must not
// be accessed after arena a is freed.
func (a *Arena) New(objPtr interface{})
// NewSlice allocates a slice from arena a. If the concrete type of slicePtr
// is *[]T, NewSlice creates a slice of element type T with the specified
// capacity whose backing store is from the arena a and stores it in
// *slicePtr. The length of the slice is set to the capacity. The slice must
// not be accessed after arena a is freed.
func (a *Arena) NewSlice(slicePtr interface{}, cap int)
这一实践已经在 Google 得到了应用,且在一些大型应用程序中节省了高达 15% 的CPU和内存使用,这主要是由于减少了垃圾收集的CPU时间和堆内存使用所带来的效果。
arena 若成为标准库的使用的例子:
import (
“arena”
…
)
type T struct {
val int
}
func main() {
a := arena.New()
var ptrT *T
a.New(&ptrT)
ptrT.val = 1
var sliceT []T
a.NewSlice(&sliceT, 100)
sliceT[99].val = 4
a.Free()
}
手动调用 arena.New 方法分配 arena 内存,再调用 Free 方法进行释放。
当然,一般提案中所提到的 arena 并不会在一门带垃圾回收的编程语言中实现。因为会操作到内存就有可能会不安全,不符合带垃圾回收的语言定义。
该库底层采取了动态检查来确保 arena 释放内存的操作是安全的。若出现异常情况,就会终止释放。
争论
围绕这这个新的提案,评论区的网友们争议的非常多。有的会疑惑,为什么一定要放在标准库,放第三方库不行吗?
实际上在第三方库中很难安全地做到这一点,因为一个在 arena 库中分配的变量,他包含指向外部的内存指针,要确保性能下让 GC 知道他,否则可能会导致错误的释放。
当然,也有人提出,这 Go 就变成像 C++ 一样,忘记 free、重复 free、提前 free,与 Go 原先标榜的简洁相差甚远。
总结
现阶段该提案还在积极探讨的阶段,原型代码也已经提交《runtime: prototype CL showing possible implementation of arenas》有兴趣的小伙伴可以抽时间看看。
这个提案争议较大,你很难说他是一个库,还是一个语言的根本性变更。你一旦在原生标准库支持了,其他关联的也必然会支持其 API,自然而然就植入进去了,与 “unsafe” 标准库定位一致,都是不安全的因素。
大家怎么看?