make:用于创建并初始化内建数据结构(slice、map、channel)。new:用于分配内存,但只返回指针,未初始化内部结构。
主要区别:
| 特性 | new | make |
|---|---|---|
| 适用类型 | 任意类型(包括基本类型、结构体等) | 仅限 slice、map、channel |
| 分配的内存大小 | 固定大小,type.size | slice:cap * type.size |
| 初始化 | 仅分配内存,字段为零值,不初始化内部结构 | 初始化切片、映射或通道,包含完整结构 |
| 底层调用 | 调用 mallocgc(type.size, ...) | 调用 mallocgc(mem, ...),mem 是动态计算 |
| 返回值 | 指针(如 *int) | 非指针,返回初始化后的类型值 |
简单例子:
-
make示例(初始化map):go 复制代码 m := make(map[string]int) m["a"] = 1 fmt.Println(m) // 输出: map[a:1] -
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 时(针对 slice、map、channel),需要分配更多的内存空间以支持这些数据结构的动态扩展,因此 make 的内存分配更加复杂。
以 slice 为例:
go
复制代码
// 示例代码
s := make([]int, 10, 20) // 创建一个长度为10,容量为20的slice
底层调用过程:
-
make调用makeslice:go 复制代码 s = makeslice(type, len, cap)- 这里的
type是slice中的元素类型(int)。 len是切片的初始长度。cap是切片的容量。
- 这里的
-
makeslice计算实际需要分配的内存大小:go 复制代码 mem := cap * type.sizecap是切片的容量。type.size是每个切片元素的大小。mem是切片底层数组需要的总内存大小。
-
调用
mallocgc为底层数组分配内存:go 复制代码 runtime.mallocgc(mem, ...)mallocgc为切片的底层数组分配一块连续的内存块(大小为mem)。- 然后返回一个指向这块内存的指针。
-
返回的切片会包含:
- 指向底层数组的指针。
- 长度 (
len)。 - 容量 (
cap)。
总结
-
make和new都使用了 Go 运行时的内存分配函数mallocgc,但:new是按类型固定分配type.size大小的内存。make是按容量动态分配更多内存,以支持slice等数据结构。
-
make的mallocgc参数中通过MulUintptr计算出实际所需内存大小,而new仅使用type.size。 -
本质区别:
new仅分配内存,不初始化复杂结构。make初始化并分配内存,支持复杂的动态数据结构。