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 库的函数签名如下:

go 复制代码
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 方法不会有任何效果。

使用例子

官方给出的最简单的基本例子。

代码如下:

go 复制代码
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 中解除绑定。

嵌套的使用例子。代码如下:

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

  • 本文作者:煎鱼
  • 公众号:脑子进煎鱼了
  • 联系方式(微信号):cJY0728(加我拉你进技术交流群)

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

相关推荐
梁梁梁梁较瘦1 天前
边界检查消除(BCE,Bound Check Elimination)
go
梁梁梁梁较瘦1 天前
指针
go
梁梁梁梁较瘦1 天前
内存申请
go
半枫荷1 天前
七、Go语法基础(数组和切片)
go
梁梁梁梁较瘦2 天前
Go工具链
go
半枫荷2 天前
六、Go语法基础(条件控制和循环控制)
go
半枫荷3 天前
五、Go语法基础(输入和输出)
go
小王在努力看博客3 天前
CMS配合闲时同步队列,这……
go
Anthony_49264 天前
逻辑清晰地梳理Golang Context
后端·go
Dobby_055 天前
【Go】C++ 转 Go 第(二)天:变量、常量、函数与init函数
vscode·golang·go