Go 基于区域的内存管理,再战手动管理!

大家好,我是煎鱼。

最近 Go 核心团队成员 @Michael Knyszek 发起针对 memory regions 的社区讨论。

2b3c459a88e66c12708b211c7aaac1a5.png

试图引入新的基于区域的内存管理(Region-based memory management),并再次之前提到的 Arena 实验给捞一下。

“基于区域的内存管理” 是什么

在计算机科学中,基于区域的内存管理(Region-Based Memory Management)是一种内存管理方式,其中每个被分配的对象都会被归属到一个特定的区域(下也称:region)。

区域也被称为区段、场地、空间或内存上下文。一个区域是多个已分配对象的集合,这些对象可以通过一次性高效的操作被整体重新分配或释放

在优势上,区域分配在内存的分配和释放上具有较低的开销!

业务背景

煎鱼注:为什么这次叫 “业务背景”。因为我感受到了他们 Google 想强烈把这个轮子放进 Go 里的冲动。所以就不是单纯的 “背景” 了。

之前有过 Arena 实验库。该类型允许直接将数据结构分配到其中,并允许批量提前释放 Arena 内存,首次提供了手动管理内存的方式。一度在圈内闹的很大。

但,作者很无语的表示:“很不幸的是,由于 Arean 与语言和标准库的兼容性较差,将 Arean 添加到标准库的提议被无限期搁置!”

0b432200ab80afead29dbfd55a9d0714.png

这次再出现,想必就是 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 本次在讨论阶段,相信很快就会进入下个阶段。我们通过本文先进行快速了解。可以继续保持关注和期待!

推荐阅读

关注和加煎鱼微信,

一手消息和知识,拉你进技术交流群👇

2e4ca15508891e0d11e4ffea778c8c58.jpeg

23300c16c3d5f50332274af4ea51b0d7.png

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

原创不易 点赞支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值