当前位置: 首页 > 科技观察

Go是否违背了它的初衷?新提案:手动内存管理

时间:2023-03-12 21:46:32 科技观察

背景由于手动内存管理一般会给程序员带来一定的精神负担,提高了编程语言的入门门槛(还记得大学写OC的时候,经常有同学写到他们倒下了……)。对应Go语言,是一种带有垃圾回收的编程语言。也就是说,不需要程序员手动管理和释放程序的内存。无需人工管理也是Go核心开发团队一直引以为豪的特点之一。最近,有人发起了一个新提案《proposal: arena: new package providing memory arenas》,引起了非常广泛的讨论。如下图所示:接下来,我们将对这个提案进行研究和理解。NewProposal本提案中提到的Arena指的是一种从连续的内存区域中分配一组内存对象的方式。优点是arena中的对象分配通常比一般的内存分配更高效,分配的对象可以一次性释放,从而最大限度地减少内存管理或垃圾回收的开销。它建议在Go的标准库中支持竞技场。标准API如下:packagearenatypeArenastruct{//containsfilteredorunexportedfields}//Newallocatesanewarena.funcNew()*Arena//Free释放arena(以及从arena分配的所有对象)以便//支持竞技场的内存可以相当快速地重用,而没有垃圾//收集开销。应用程序不得在这个//arena被释放后调用任何方法。func(a*Arena)Free()//New从arenaa分配一个对象。如果objPtr的具体类型是//指向类型T(**T)的指针,则New分配//T类型的对象并将指向该对象的指针存储在*objPtr中。释放arenaa后不得//访问该对象。func(a*Arena)New(objPtrinterface{})//NewSlice从arenaa分配一个切片。如果slicePtr//的具体类型是*[]T,NewSlice创建一个具有指定//容量的元素类型T的切片,其后备存储来自arenaa并将其存储在//*slicePtr中。切片的长度是s等容量。arenaa被freed后必须//不能访问slice.func(a*Arena)NewSlice(slicePtrinterface{},capint)这种做法在Google已经应用,在一些大型应用中节省高达15%的CPU和程序中的内存占用,主要是为了垃圾回收减少CPU时间和堆内存占用的效果如果用arena作为标准库例子:import("arena"...)typeTstruct{valint}funcmain(){a:=arena.New()varptrT*Ta.New(&ptrT)ptrT.val=1varsliceT[]Ta.NewSlice(&sliceT,100)sliceT[99].val=4a.Free()}手动调用arena.New方法分配arena内存,然后调用Free方法释放。当然,一般提案中提到的arena不会用带有垃圾回收的编程语言来实现。因为会操作内存,所以可能不安全,不符合垃圾回收的语言定义。库底层采用动态校验,确保arena释放内存的操作是安全的。若出现异常情况,将终止发布。争议围绕这个新提案,评论区的网友们争论不休。可能有人会疑惑,为什么一定要放在标准库中,不能放在第三方库中吗?事实上,在第三方库中很难安全地做到这一点,因为在arena库中分配的一个变量包含指向外部内存的指针。一定要保证性能,让GC知道他,否则可能会导致错误释放。当然,也有人提出,这个Go变得像C++一样,忘记free,重复free,提前free,与Go最初标榜的简单相去甚远。总结现阶段提案还在积极讨论阶段,原型代码也已经提交。《runtime: prototype CL showing possible implementation of arenas》有兴趣的朋友可以抽空看看。这个提议颇有争议。很难说这是一个库还是语言的根本变化。一旦你在原生标准库中支持了它,其他相关的API必然会支持它的API,自然而然的就会被嵌入其中。与“不安全”标准库的定位一致,属于不安全因素。你怎么认为?