Go语言new和make的区别

方法作用作用对象返回值
new分配内存值类型和用户定义的类型初始化为零值,返回指针
make分配内存内置引用类型(map,slice,channel)初始化为零值,返回引用类型本身

1.1 new的主要特性

new是内建函数,可以参考如下代码定义:

func new(Type) *Type 

内建函数new用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值得指针。

根据上述的描述,可以自己实现一个类型new的功能:

func newInt() *int {
  var i int
  return &i
}

someInt := newInt()

这个函数的功能和somInt := new(int)一模一样,所以我们在定义new开头的函数时,出于约定也应该返回类型的指针。

1.2 make的主要特性

make也是内建函数,它的定义比new多了一个参数,返回值也不同。可以参考如下的代码定义:

func make(Type, size IntegerType) Type

make是用来初始化map,slice,channel这几种特定类型的。编译过程中,用make去初始化不同的类型会调用不同的底层函数:

  • 1.初始化map,调用runtime.makemap
  • 2.初始化slice,调用runtime.makeslice
  • 3.初始化channel,调用runtime.makechan

接下来了解下函数的源码:

  • runtime.makemap
// path: src/runtime/map.go
func makemap(t *maptype, hint int, h *hmap) *hmap {
    ...
   // 初始化 Hmap
   if h == nil {
      h = new(hmap)
   }

   // 生成 hash 种子
   h.hash0 = fastrand()

   // 计算 桶 的数量
   B := uint8(0)
   for overLoadFactor(hint, B) {
      B++
   }
   h.B = B
   if h.B != 0 {
      var nextOverflow *bmap

      // 创建 桶
      h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
      ...
   }
   return h
}
  • runtime.makeslice
// path: src/runtime/slice.go
func makeslice(et *_type, len, cap int) unsafe.Pointer {
    // 计算占用空间和是否溢出
    mem, overflow := math.MulUintptr(et.size, uintptr(cap))

   // 一些边界条件处理 
    if overflow || mem > maxAlloc || len < 0 || len > cap {
        mem, overflow := math.MulUintptr(et.size, uintptr(len))
        if overflow || mem > maxAlloc || len < 0 {
           // panic: len 超出范围 
            panicmakeslicelen()
        }
        // panic: cap 超出范围 
        panicmakeslicecap()
    }

    return mallocgc(mem, et, true)
}
  • runtime.makechan
// path: src/runtime/chan.go
func makechan(t *chantype, size int) *hchan {
   ...
   var c *hchan

   // 针对不同情况下对 channel 实行不同的内存分配策略
   switch {
   case mem == 0:
      // 无缓冲区,只给 hchan 分配一段内存
      c = (*hchan)(mallocgc(hchanSize, nil, true))
      c.buf = c.raceaddr()
   case elem.ptrdata == 0:
      // channel 不包含指针,给 hchan 和 缓冲区分配一段连续的内存
      c = (*hchan)(mallocgc(hchanSize+mem, nil, true))
      c.buf = add(unsafe.Pointer(c), hchanSize)
   default:
      // 单独给 hchan 和 缓冲区分配内存
      c = new(hchan)
      c.buf = mallocgc(mem, elem, true)
   }

   // 初始化 hchan 的内部字段 
   c.elemsize = uint16(elem.size)
   c.elemtype = elem
   c.dataqsiz = uint(size)
   ...
}

1.3 总结

make相对于new来说,做的事情更多,new只是开辟了内存空间, make为更加复杂的数据结构开辟内存空间并对一些字段进行初始化