本提案所提到的 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” 標準庫定位一致,都是不安全的因素。
大家怎么看?