Go 要違背初心嗎?新提案:手動管理內存

語言: CN / TW / HK

大家好,我是煎魚。

前段 SZ 疫情嚴重的很,公司所在的大廈都被封了 2~3 次...在家中搬磚的我,聽説 Go 要變成 C++ 了?大為震驚。

今天就由煎魚帶大家看看背後的那個提案。

(結果我發這文時,這周直接變成居家辦公 7 天了,車也不能隨便開出去了...)

背景

由於手動管理內存普遍會給程序員帶來一定的心智負擔,提高一門編程語言的入門門檻(還記得大學寫 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” 標準庫定位一致,都是不安全的因素。

大家怎麼看?以後是不是可以玩出新的花樣了...

關注煎魚,獲取業內第一手消息和知識 :point_down:

你好,我是煎魚, 出版過 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領域最有觀點專家)榮譽, 點擊藍字查看我的出書之路

日常分享高質量文章,輸出 Go 面試、工作經驗、架構設計, 加微信拉讀者交流羣,和大家交流!