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