go 1.4 引入栈动态扩容机制,当 goroutine 栈空间不足时,运行时会分配新栈并复制旧栈内容。这引发了一个常见误解:为避免“栈复制”开销,应将大数组(如 [8096]int)声明为全局变量。但事实并非如此——栈复制仅发生在栈增长临界点(极低频),且现代 go 运行时已大幅优化该过程;相比之下,栈内存访问速度远高于堆,且局部变量天然具
备清晰生命周期与作用域边界。
关键原则:语义优先,性能其次
Go 是词法作用域语言,变量定义位置首先应反映其逻辑归属:
实测佐证(Go 1.21+)
func BenchmarkLocalArray(b *testing.B) {
for i := 0; i < b.N; i++ {
var buf [8096]int
for j := range buf {
buf[j] = j
}
}
}
func BenchmarkGlobalArray(b *testing.B) {
for i := 0; i < b.N; i++ {
for j := range globalBuf { // globalBuf 是包级变量
globalBuf[j] = j
}
}
}基准测试通常显示局部数组版本更快(栈分配零成本,无指针追踪开销),而全局变量因需在程序启动时初始化、且长期驻留内存,反而增加 GC 压力。
注意事项
总之,坚持“变量最小作用域”原则——让 buf 安静地待在 foo 函数里,既是 Go 的惯用之道,也是更安全、更高效的选择。