Go1.20 arena 能手动管理内存了,怎么用?

最近 Go1.20 中的手动管理内存受到了很多人的关注。众所周知,Go 是一门带垃圾回收(GC)的编程语言,可以进行自动的内存申请、释放等内存操作。

最近 Go1.20 中的手动管理内存受到了很多人的关注。众所周知,Go 是一门带垃圾回收(GC)的编程语言,可以进行自动的内存申请、释放等内存操作。

大家好,我是煎鱼。

最近 Go1.20 中的手动管理内存受到了很多人的关注。众所周知,Go 是一门带垃圾回收(GC)的编程语言,可以进行自动的内存申请、释放等内存操作。

带 GC 能简化编程时的心智成本,也保证了内存的安全。我们说 “一般”,也就是有例外。人们说六个,一般都有七个。

Go 的例外就出现了。

Go1.20 arena

新版本 Go1.20,基于 Google 自身的需求,快速通过了实践,正式支持了 arena,能够实现手动的内存管理(当前是实验性特性)。

现在可以通过GOEXPERIMENT=arenas环境变量启用:

GOEXPERIMENT=arenas go run main.go

该特性可以让程序员手动的从一个连续的内存区域申请、分配一组内存对象,也可以一次性的释放。

重点是可以手动管理内存。

提供的 arena API

  • NewArena:创建一个新的 arena 内存空间。
  • Free:释放 arena 及其关联对象。
  • New:基于 arena,创建新对象。
  • MakeSlice:基于 arena,创建新切片。
  • Clone:克隆一个 arena 的对象,并移动到内存堆上。

一些 arena 例子

以下案例和性能测试是基于 uptrace 在Golang memory arenas [101 guide][1]中分享的 arena 例子,本处进行引用,我就不自创一份了。

很适合在初学时作为 Demo 使用,打算也留着自己下次用时结合文档翻一番。

arena.NewArena

一起来快速入门。代码如下:

import "arena"

type T struct{
Foo string
Bar [16]byte
}

func processRequest(req *http.Request) {
// 在函数开头创建一个 arena
mem := arena.NewArena()
// 在函数结束时释放 arena
defer mem.Free()

// 从申请的 arena 中申请一些对象
for i := 0; i < 10; i++ {
obj := arena.New[T](mem "T")
}

// 从申请的 arena 中申请切片对象(指定长度和容量)
slice := arena.MakeSlice[T](mem, 100, 200 "T")
}

arena.Clone

如果要单独使用某个申请出来的对象。可以借助 Clone 方法进行单独处理。

如下代码:

// 创建一个 arena
mem := arena.NewArena()

obj1 := arena.New[T](mem "T") // 分配一个 arena 对象
obj2 := arena.Clone(obj1) // 拷贝一个 arena 上的对象,移动到内存堆上
fmt.Println(obj2 == obj1) // 即使是基于拷贝出来的,两者并不完全等价

// 释放 arena,obj1 不可使用,obj2 可正常使用
mem.Free()

释放了最早申请的 arena,Clone 方法在这里将会把 obj1 拷贝到新的内存堆上,再赋值给 obj2。后续要单独用 obj2 就可以继续使用。

reflect.ArenaNew

也可以结合 arena 和 reflect 两个标准库来进行使用。如下代码:

var typ = reflect.TypeOf((*T)(nil)).Elem()

mem := arena.NewArena()
defer mem.Free()

value := reflect.ArenaNew(mem, typ)
fmt.Println(value.Interface().(*T))

arena.MakeSlice

该方法的常规用法:

arena.MakeSlice[string](mem, length, capacity "string")

如果需要申请一个新切片并追加元素:

slice := arena.MakeSlice[string](mem, 0, 0 "string")
slice = append(slice, "")

需要注意的是,arena 目前不支持 map。但你可以通过泛型来实现类似的效果。

arena.String

原则上 arena 不支持 string。但是我们依然可以通过 unsafe.String 方法的骚操作来变相实现。

如下代码:

src := "脑子进煎鱼了"

mem := arena.NewArena()
defer mem.Free()

bs := arena.MakeSlice[byte](mem, len(src "byte"), len(src))
copy(bs, src)
str := unsafe.String(&bs[0], len(bs))

在申请的 arena 释放后,该对应的 string 就无法使用了,需要特别注意。

性能表现

这个允许手工管理内存的 arena 的特性是来源于内部,提案也是一路绿灯通过。(懂得懂)。

自述已经为 Google 许多应用节省了高达 15% 的 CPU 和内存使用量,主要原因是减少了垃圾收集 CPU 时间和堆内存使用量。

经过在vmihailenco/golang-memory-arenas[2]项目中实际的性能对比。

没有用 arena:

/usr/bin/time go run arena_off.go
77.27user 1.28system 0:07.84elapsed 1001%CPU (0avgtext+0avgdata 532156maxresident)k
30064inputs+2728outputs (551major+292838minor)pagefaults 0swaps

使用了 arena:

GOEXPERIMENT=arenas /usr/bin/time go run arena_on.go
35.25user 5.71system 0:05.09elapsed 803%CPU (0avgtext+0avgdata 385424maxresident)k
48inputs+3320outputs (417major+63931minor)pagefaults 0swaps

使用了 arena 的代码运行速度更快,且使用的内存更少。

总结

Go 的各位大大们在性能优化中,不断地试图压榨 Go 的潜力。现在已经到了手工管理内存的阶段了。

实际的测试结果来看,是有作用的。

有兴趣的小伙伴可以在 Go1.20 起就开始试用。不过需要注意,该特性由于发现了严重的 API 问题(想把 arena 应用到其他的标准库中,但这是个大事件),社区还需要认真思考后续的发展,现阶段处于处于停滞状态。

从这次提案来看,真的是,内部需求一路猛如虎,直接冲上 master。外部需求就畏畏缩缩了。真双标?

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月6日 22:23
下一篇 2023年5月6日 22:23

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信