4.make和new
当我们想要在 Go 语言中初始化一个结构时,可能会用到两个不同的关键字 make
和 new
。因为它们的功能相似,所以初学者可能会对这两个关键字的作用感到困惑,但是它们两者能够初始化的变量却有较大的不同。
make
的作用是初始化内置的数据结构,也就是我们在前面提到的切片、哈希表和 Channel;new
的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针;
slice
是一个包含data
、cap
和len
的结构体reflect.SliceHeader
;hash
是一个指向runtime.hmap
结构体的指针;ch
是一个指向runtime.hchan
结构体的指针;
相比与复杂的 make
关键字,new
的功能就简单多了,它只能接收类型作为参数然后返回一个指向该类型的指针:
上述代码片段中的两种不同初始化方法是等价的,它们都会创建一个指向 int
零值的指针。
make
在编译期间的类型检查阶段,Go 语言会将代表 make
关键字的 OMAKE
节点根据参数类型的不同转换成了 OMAKESLICE
、OMAKEMAP
和 OMAKECHAN
三种不同类型的节点,这些节点会调用不同的运行时函数来初始化相应的数据结构。
new
编译器会在中间代码生成阶段通过以下两个函数处理该关键字:
cmd/compile/internal/gc.callnew
会将关键字转换成ONEWOBJ
类型的节点;cmd/compile/internal/gc.state.expr
会根据申请空间的大小分两种情况处理:如果申请的空间为 0,就会返回一个表示空指针的
zerobase
变量;在遇到其他情况时会将关键字转换成
runtime.newobject
函数:
无论是直接使用 new
,还是使用 var
初始化变量,它们在编译器看来都是 ONEW
和 ODCL
节点。
如果变量会逃逸到堆上,这些节点在这一阶段都会被
cmd/compile/internal/gc.walkstmt
转换成通过runtime.newobject
函数并在堆上申请内存;如果变量不需要在当前作用域外生存,例如不用作为返回值返回给调用方,那么就不需要初始化在堆上。
runtime.newobject
函数会获取传入类型占用空间的大小,调用 runtime.mallocgc
在堆上申请一片内存空间并返回指向这片内存空间的指针:
runtime.mallocgc
函数的实现大概有 200 多行代码,我们会在后面的章节中详细分析 Go 语言的内存管理机制。
小结
这里我们简单总结一下 Go 语言中 make
和 new
关键字的实现原理,make
关键字的作用是创建切片、哈希表和 Channel 等内置的数据结构,并初始化数据结构内置字段;而 new
的作用是为类型申请一片内存空间,并返回指向这片内存的指针。
原文链接
https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-make-and-new/
Last updated