new()仅清零内存而不初始化运行时结构,返回非nil但未就绪的指针;make()专为slice、map、chan设计,完成内存分配与运行时初始化。

new() 只清零内存,不初始化运行时结构
当你写 new(map[string]int) 或 new([]int),Go 确实分配了一块内存,并把每个字节设为 0,但这就结束了。它不会调用 runtime.makemap,也不会为 slice 设置 len/cap 字段或关联底层数组。结果是:返回一个非 nil 的指针,但指向的仍是“未就绪”状态。
常见错误现象:panic: assignment to entry in nil map 或 panic: index out of range,就因为误以为 new 返回的是可用对象。
-
new(T)中T必须是具体类型(如struct、int),不能是接口或未定义类型 - 对
slice/map/chan用new,得到的是 nil 指针,不能直接调用方法或赋值 - 底层调用
mallocgc(size, typ, true),只做堆分配 + 清零,无构造逻辑
make() 才真正完成运行时初始化
make() 不是通用内存分配器,它是专为 slice、map、chan 三类内建类型设计的初始化函数。它不止分配内存,还触发对应运行时初始化逻辑:比如为 map 构建哈希桶数组、为 slice 分配底层数组并填好 len/cap、为 chan 初始化锁和等待队列。
使用场景很明确:只要你要立刻 m["k"] = v、s[i] = x、ch ,就必须用 <code>make。
立即学习“go语言免费学习笔记(深入)”;
-
make([]int, 5)返回长度为 5 的切片,底层数组已就位;make(map[string]int)返回可立即写入的 map -
make(chan int, 10)创建带缓冲区的 channel;make(map[K]V, hint)的hint是哈希桶预分配提示,不是“容量”语义 - 对非这三类类型(如
struct、*int)调用make会编译报错:cannot make type T
返回值类型与使用方式完全不同
new(T) 总是返回 *T —— 一个指向零值的指针;make(T, ...) 返回的是 T 本身(即 []int、map[string]int、chan int),不是指针。这个差异直接影响你怎么写代码。
比如你想传参给一个接受 map[string]int 的函数,用 make 得到的值可以直接传;而用 new 得到的是 *map[string]int,必须解引用才能用,且仍 panic。
-
func f(m map[string]int) { m["x"] = 1 }→ 正确调用:f(make(map[string]int)),错误调用:f(*new(map[string]int)(运行时 panic) - 对基本类型(
int、string、自定义struct)只能用new;对slice/map/chan只能用make—— 二者没有重叠适用域 - 不要试图用
new配合append模拟切片创建:s := new([]int); *s = append(*s, 1)虽然能跑,但绕过 Go 的惯用法,且每次append都可能重新分配,性能不可控
底层分配器视角:mallocgc vs makemap/makeslice
从 Go 运行时角度看,new(T) 最终走的是 mallocgc(typ.size, typ, true),纯堆分配 + 清零;而 make(map[K]V) 会进一步调用 runtime.makemap,后者根据 key/value 类型计算哈希参数、预分配桶数组、初始化 hmap 结构体字段——这才是 map 能用的根本。
同理,make([]int, 5) 会调用 runtime.makeslice,它不仅分配底层数组,还构造 slice 头部(含 len/cap/指针),并确保 GC 能正确追踪底层数组生命周期。
- 性能上,
new几乎无开销;make有额外初始化成本,但这是必要代价 —— 没它,map就只是个空壳 - 兼容性无差异,但混淆二者会导致 runtime panic,这类错误在 CI 或线上才暴露,调试成本高
- 最容易被忽略的一点:
var m map[string]int和m := new(map[string]int表面不同,实际都导致后续写操作 panic —— 唯一安全路径是显式make


















