5.5 make and new

Kesa...大约 2 分钟golang

makenew都用于初始化变量:

  • make 的作用是初始化内置的数据结构
    • 切片
    • 哈希表
    • Channel
  • new的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针
golang-make-and-new
golang-make-and-new

5.5.1 make

golang-make-typecheck
golang-make-typecheck

编译期会将make转换成OMAKE节点,并根据参数不同,转换成:

  • OMAKESLICE:初始化slice
  • OMAKEMAP:初始化哈希表
  • OMAKECHAN:初始化channel

5.5.2 new

编译期会在中间代码生成阶段使用两个函数处理:

  1. cmd/compile/internal/gc.callnewopen in new window 会将关键字转换成 ONEWOBJ 类型的节点
  2. cmd/compile/internal/gc.state.expropen in new window 会根据申请空间的大小分两种情况处理:
    1. 如果申请的空间为 0,就会返回一个表示空指针的 zerobase 变量
    2. 在遇到其他情况时会将关键字转换成 runtime.newobjectopen in new window 函数
func callnew(t *types.Type) *Node {
	...
	n := nod(ONEWOBJ, typename(t), nil)
	...
	return n
}

func (s *state) expr(n *Node) *ssa.Value {
	switch n.Op {
	case ONEWOBJ:
		if n.Type.Elem().Size() == 0 {
			return s.newValue1A(ssa.OpAddr, n.Type, zerobaseSym, s.sb)
		}
		typ := s.expr(n.Left)
		vv := s.rtcall(newobject, true, []*types.Type{n.Type}, typ)
		return vv[0]
	}
}

需要注意的是,无论是直接使用 new,还是使用 var 初始化变量,它们在编译器看来都是 ONEWODCL 节点。

如果变量会逃逸到堆上,这些节点在这一阶段都会被 cmd/compile/internal/gc.walkstmtopen in new window 转换成通过 runtime.newobjectopen in new window 函数并在堆上申请内存:

func walkstmt(n *Node) *Node {
	switch n.Op {
	case ODCL:
		v := n.Left
		if v.Class() == PAUTOHEAP {
			if prealloc[v] == nil {
				prealloc[v] = callnew(v.Type)
			}
			nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v])
			nn.SetColas(true)
			nn = typecheck(nn, ctxStmt)
			return walkstmt(nn)
		}
	case ONEW:
		if n.Esc == EscNone {
			r := temp(n.Type.Elem())
			r = nod(OAS, r, nil)
			r = typecheck(r, ctxStmt)
			init.Append(r)
			r = nod(OADDR, r.Left, nil)
			r = typecheck(r, ctxExpr)
			n = r
		} else {
			n = callnew(n.Type.Elem())
		}
	}
}

如果通过 var 或者 new 创建的变量不需要在当前作用域外生存,例如不用作为返回值返回给调用方,那么就不需要初始化在堆上。

runtime.newobjectopen in new window 函数会获取传入类型占用空间的大小,调用 runtime.mallocgcopen in new window 在堆上申请一片内存空间并返回指向这片内存空间的指针:

func newobject(typ *_type) unsafe.Pointer {
	return mallocgc(typ.size, typ, true)
}

5.5.3 小结

makenew 关键字的实现原理:

  • make 关键字的作用是创建切片、哈希表和 Channel 等内置的数据结构
  • new 的作用是为类型申请一片内存空间,并返回指向这片内存的指针

Reference

  1. https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-make-and-new/open in new window
上次编辑于:
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.2