Go1.20 arena 能手動管理內存了,怎么用?
大家好,我是煎魚。
最近 Go1.20 中的手動管理內存受到了很多人的關注。眾所周知,Go 是一門帶垃圾回收(GC)的編程語言,可以進行自動的內存申請、釋放等內存操作。
帶 GC 能簡化編程時的心智成本,也保證了內存的安全。我們說 “一般”,也就是有例外。人們說六個,一般都有七個。
Go 的例外就出現了。
Go1.20 arena
新版本 Go1.20,基于 Google 自身的需求,快速通過了實踐,正式支持了 arena,能夠實現手動的內存管理(當前是實驗性特性)。
現在可以通過 GOEXPERIMENT=arenas 環境變量啟用:
該特性可以讓程序員手動的從一個連續的內存區域申請、分配一組內存對象,也可以一次性的釋放。
重點是可以手動管理內存。
提供的 arena API
- NewArena:創建一個新的 arena 內存空間。
- Free:釋放 arena 及其關聯對象。
- New:基于 arena,創建新對象。
- MakeSlice:基于 arena,創建新切片。
- Clone:克隆一個 arena 的對象,并移動到內存堆上。
一些 arena 例子
以下案例和性能測試是基于 uptrace 在 Golang memory arenas [101 guide][1] 中分享的 arena 例子,本處進行引用,我就不自創一份了。
很適合在初學時作為 Demo 使用,打算也留著自己下次用時結合文檔翻一番。
arena.NewArena
一起來快速入門。代碼如下:
arena.Clone
如果要單獨使用某個申請出來的對象。可以借助 Clone 方法進行單獨處理。
如下代碼:
釋放了最早申請的 arena,Clone 方法在這里將會把 obj1 拷貝到新的內存堆上,再賦值給 obj2。后續要單獨用 obj2 就可以繼續使用。
reflect.ArenaNew
也可以結合 arena 和 reflect 兩個標準庫來進行使用。如下代碼:
arena.MakeSlice
該方法的常規用法:
如果需要申請一個新切片并追加元素:
需要注意的是,arena 目前不支持 map。但你可以通過泛型來實現類似的效果。
arena.String
原則上 arena 不支持 string。但是我們依然可以通過 unsafe.String 方法的騷操作來變相實現。
如下代碼:
在申請的 arena 釋放后,該對應的 string 就無法使用了,需要特別注意。
性能表現
這個允許手工管理內存的 arena 的特性是來源于內部,提案也是一路綠燈通過。(懂得懂)。
自述已經為 Google 許多應用節省了高達 15% 的 CPU 和內存使用量,主要原因是減少了垃圾收集 CPU 時間和堆內存使用量。
經過在 vmihailenco/golang-memory-arenas[2] 項目中實際的性能對比。
沒有用 arena:
使用了 arena:
使用了 arena 的代碼運行速度更快,且使用的內存更少。
總結
Go 的各位大大們在性能優化中,不斷地試圖壓榨 Go 的潛力。現在已經到了手工管理內存的階段了。
實際的測試結果來看,是有作用的。
有興趣的小伙伴可以在 Go1.20 起就開始試用。不過需要注意,該特性由于發現了嚴重的 API 問題(想把 arena 應用到其他的標準庫中,但這是個大事件),社區還需要認真思考后續的發展,現階段處于處于停滯狀態。
從這次提案來看,真的是,內部需求一路猛如虎,直接沖上 master。外部需求就畏畏縮縮了。真雙標?