golang的make与new区别

178 阅读3分钟
  • make:用于创建并初始化内建数据结构(slicemapchannel)。
  • new:用于分配内存,但只返回指针,未初始化内部结构。

主要区别:

特性newmake
适用类型任意类型(包括基本类型、结构体等)仅限 slicemapchannel
分配的内存大小固定大小,type.sizeslicecap * type.size
初始化仅分配内存,字段为零值,不初始化内部结构初始化切片、映射或通道,包含完整结构
底层调用调用 mallocgc(type.size, ...)调用 mallocgc(mem, ...)mem 是动态计算
返回值指针(如 *int非指针,返回初始化后的类型值

简单例子:

  1. make 示例(初始化 map):

    go
    复制代码
    m := make(map[string]int)
    m["a"] = 1
    fmt.Println(m) // 输出: map[a:1]
    
  2. new 示例(分配 int 内存):

    go
    复制代码
    p := new(int)   // 分配一个 int 指针,初始值为 0
    *p = 42
    fmt.Println(*p) // 输出: 42
    

错误示范

package main
import "fmt"

type Node struct {
	Next *int
	Val  int
}

func main() {
	node := new(Node)
	node.Val = 4
	fmt.Println(node.Val)
        // 错误用法
	*(node.Next) = 5
	fmt.Println(node.Next)
   // 正确用法
   value := 5 
   node.Next = &value 
   // Next 指向 value 的地址 or 
   // node.Next = new(int),
   // *(node.Next) = 5
   fmt.Println(*(node.Next)) // 输出: 5
}

上文定义来一个结构体 Node,内部含有一个指针Next和Val,使用new为Node分配内存,new不会初始化结构体内部指针结构,从内存层面也就是只为最外层分配了空间比如Next是个指针和Val是个值。

  • Next 是一个指针,占用指针大小,默认值为 nil

  • Val 是一个整数,占用 int 大小,默认值为 0

下面从底层函数实现角度出发

mallocgc 的作用

mallocgc 是 Go 运行时提供的核心内存分配函数,用于申请一块指定大小的内存,并根据需求进行垃圾回收标记。它是 Go 中所有动态内存分配操作的底层支撑。


2. new 的底层调用流程

当使用 new 时,Go 的底层会直接分配 type.size 大小的内存,type.size 是被分配类型的固定大小。这意味着 new 分配的内存仅足够容纳类型所需的空间,未初始化任何复杂结构。

代码流程分析(伪代码):

go
复制代码
// 示例代码
ptr := new(int) // 分配一个 int 类型的内存
  • new 会调用 runtime.mallocgc

    go
    复制代码
    runtime.mallocgc(size=type.size, ...)
    
  • mallocgc 仅分配一块足够大小的内存并返回指针。

  • 对应到 int,只需要分配 type.size(4 字节或 8 字节,取决于系统架构)。


3. make 的底层调用流程

当使用 make 时(针对 slicemapchannel),需要分配更多的内存空间以支持这些数据结构的动态扩展,因此 make 的内存分配更加复杂。

slice 为例:

go
复制代码
// 示例代码
s := make([]int, 10, 20) // 创建一个长度为10,容量为20的slice

底层调用过程:

  1. make 调用 makeslice

    go
    复制代码
    s = makeslice(type, len, cap)
    
    • 这里的 typeslice 中的元素类型(int)。
    • len 是切片的初始长度。
    • cap 是切片的容量。
  2. makeslice 计算实际需要分配的内存大小:

    go
    复制代码
    mem := cap * type.size
    
    • cap 是切片的容量。
    • type.size 是每个切片元素的大小。
    • mem 是切片底层数组需要的总内存大小。
  3. 调用 mallocgc 为底层数组分配内存:

    go
    复制代码
    runtime.mallocgc(mem, ...)
    
    • mallocgc 为切片的底层数组分配一块连续的内存块(大小为 mem)。
    • 然后返回一个指向这块内存的指针。
  4. 返回的切片会包含:

    • 指向底层数组的指针。
    • 长度 (len)。
    • 容量 (cap)。

总结

  1. makenew 都使用了 Go 运行时的内存分配函数 mallocgc,但:

    • new 是按类型固定分配 type.size 大小的内存。
    • make 是按容量动态分配更多内存,以支持 slice 等数据结构。
  2. makemallocgc 参数中通过 MulUintptr 计算出实际所需内存大小,而 new 仅使用 type.size

  3. 本质区别

    • new 仅分配内存,不初始化复杂结构。
    • make 初始化并分配内存,支持复杂的动态数据结构。

引用

深入理解make和new,从底层到应用!