Go 基于區域的內存管理,再戰手動管理!
大家好,我是煎魚。
最近 Go 核心團隊成員 @Michael Knyszek 發起針對 memory regions 的社區討論。
圖片
試圖引入新的基于區域的內存管理(Region-based memory management),并再次之前提到的 Arena 實驗給撈一下。
“基于區域的內存管理” 是什么
在計算機科學中,基于區域的內存管理(Region-Based Memory Management)是一種內存管理方式,其中每個被分配的對象都會被歸屬到一個特定的區域(下也稱:region)。
區域也被稱為區段、場地、空間或內存上下文。一個區域是多個已分配對象的集合,這些對象可以通過一次性高效的操作被整體重新分配或釋放。
在優勢上,區域分配在內存的分配和釋放上具有較低的開銷!
業務背景
煎魚注:為什么這次叫 “業務背景”。因為我感受到了他們 Google 想強烈把這個輪子放進 Go 里的沖動。所以就不是單純的 “背景” 了。
之前有過 Arena 實驗庫。該類型允許直接將數據結構分配到其中,并允許批量提前釋放 Arena 內存,首次提供了手動管理內存的方式。一度在圈內鬧的很大。
但,作者很無語的表示:“很不幸的是,由于 Arean 與語言和標準庫的兼容性較差,將 Arean 添加到標準庫的提議被無限期擱置!”
圖片
這次再出現,想必就是 Google 團隊里的 Go 同學還是想再試試。
新提案
提案背景
在原有的 Arean 提案設計中,應用的 API 要使用 arean,必須接受一個額外的參數:要分配到哪個 arean。和 context 類似。
有太多的應用程序 API 需要更新才能很好地與 Go 語言的編寫方式集成,而且這會讓這些應用程序接口變得更糟糕。這也是最終被很多人反對的原因之一。
因此本次新提案提出了一種可組合的方法,即以用戶定義的 goroutine-local 內存區域的形式來替代原先 arena 的方式。
具體設計
本次新提案提出的是新的庫,造一個新輪子去覆蓋老的輪子(:doge
region 庫的函數簽名如下:
package region
func Do(f func())
func Ignore(g func())
一共包含兩個方法??雌饋砗苌?,但有一定的 “學問” 在。
以下具體講講兩個方法的作用和使用方向。
1、Do 方法
函數作用:該方法創建一個新的 region,并在該 region 中調用參數 f(閉包函數)。當 Do 返回時,該 region 會被銷毀。
核心特性如下:
- 隱式內存綁定:在 f 及其調用鏈中分配的內存可能會被隱式綁定到當前的 region。
- 自動解綁:內存在特定場景會自動從 region 中解綁,例如:
該內存被其他 region 引用。
被其他 goroutine 或調用方(包括 Do 的調用者及其上層調用者)引用;或被其他未綁定到此 region 的內存引用。
- 資源回收:如果 region 被銷毀時仍有內存綁定到它,這些內存將由 runtime 主動回收。
- 性能優化:
- 正確使用時,可通過內存復用降低資源成本,減輕垃圾回收(GC)的壓力。
- 錯誤使用可能增加資源成本,因為從 region 中解綁內存也有代價。
- 局部性:
- region 僅對創建它的 goroutine 有效,不能傳播到新創建的 goroutine。
- 異常處理:
- 當 f 的執行因為 panic 或調用 runtime.Goexit 終止時,region 會像正常返回一樣銷毀。
2、Ignore 方法
函數作用:該方法讓 g 及其調用鏈忽略當前 goroutine 上已激活使用的 region。用于排除已知生命周期長于 region 的內存,從而更高效地利用 region。
性能上,使用 Ignore 主動排除內存比自動解綁更高效。作為兜底邏輯,在沒有激活 region 的情況下調用 Ignore 方法不會有任何效果。
使用例子
官方給出的最簡單的基本例子。
代碼如下:
var keep *int
region.Do(func() {
w := new(int)
x := new(MyStruct)
y := make([]int, 10)
z := make(map[string]string)
*w = use(x, y, z)
keep = w // w 從 region 中解除綁定
}) // x、y 和 z 的內存會被緊急清理,而 w 則不會。
這個例子想表述的是:所有主要的內置函數都適用于 region 功能。而且從 region 中泄漏的指針會導致其指向的內存從 region 中解除綁定。
嵌套的使用例子。代碼如下:
region.Do(func() {
z := new(MyStruct)
var y *MyStruct
region.Do(func() {
x := new(MyStruct)
use(x, z) // z 可在該內部 region 內自由使用
y = x // x 不受任何 region 的約束
})
use(y)
})
這個例子主要演示 region 嵌套 region 的使用。
總結
這次 Go 核心團隊想要引入支持手動做內存管理的決心感覺非常大。畢竟 arean 被 ban 了后又沉淀了一段時間,馬上又推出了 region 的新提案。
Region 本次在討論階段,相信很快就會進入下個階段。我們通過本文先進行快速了解??梢岳^續保持關注和期待!