大家好,我是煎鱼。
最近 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 本次在讨论阶段,相信很快就会进入下个阶段。我们通过本文先进行快速了解。可以继续保持关注和期待!