1. 栈和堆
Heap:内存的分配和释放都由编译器进行管理,分配和释放的速度非常快
Stack:不会自动清理,会引起频繁地GC操作,而垃圾回收操作会占用较大的系统开销
2. 逃逸分析
逃逸分析是对变量放在堆上还是栈上的分析,该分析在编译阶段完成。
如果一个变量超过了函数调用的生命周期,即该变量在函数外部存在引用,编译器会把这个变量分配到堆上,此时该变量发生了逃逸。
go run -gcflags '-m -l' main.go
-m print optimization decisions 代码编译优化情况,包括逃逸情况和函数是否内联
-l disable inlining
补充:
-gcflags
: 给编译器传递参数,go tool compile --help
查看可用参数
-ldflags
: 给链接器传递参数,go tool link --help
查看可用参数
3. 逃逸场景
3.1 interface{}
赋值
type Animal struct {
Name interface{}
}
func main() {
a := new(Animal)
a.Name = "cat"
}
编译结果:
$ go build -gcflags '-m -l' ./main.go
./main.go:8:10: new(Animal) does not escape
./main.go:9:9: "cat" escapes to heap
优化方案:将 interface{} 改为确定类型
type Animal struct {
Name interface{}
}
func main() {
a := new(Animal)
a.Name = "cat"
}
再次编译:
$ go build -gcflags '-m -l' ./main.go
./main.go:8:10: new(Animal) does not escape
3.2 传递指针
type Animal struct {
Name string
}
func NewAnimal(name string) *Animal {
return &Animal{
Name: name,
}
}
func main() {
NewAnimal("dog")
}
编译结果:
$ go build -gcflags '-m -l' ./main.go
./main.go:7:16: leaking param: name
./main.go:8:9: &Animal{...} escapes to heap
结论:指针做为参数的好处在于减少值拷贝,但变量逃逸的开销可能会更大
3.3 栈空间不足
func main() {
arr := make([]int, 10000)
for i := range arr {
arr[i] = i
}
}
编译结果:
$ go build -gcflags '-m -l' ./main.go
./main.go:4:13: make([]int, 10000) escapes to heap
4. 强制避免逃逸
// $GOROOT/src/runtime/stubs.go
func noescape(p unsafe.Pointer) unsafe.Pointer {
x := uintptr(p)
return unsafe.Pointer(x ^ 0) // 任何数值与0的异或都是原数
}
Go标准库和运行时实现中,大量使用函数noescape
函数。其实现逻辑:将传入的指针转换为uintptr
数值类型,然后再次转换为指针。转换过程切断了逃逸分析的数据流跟踪,导致传入的指针避免逃逸
type Animal struct {
Name string
}
func NewAnimal(name string) unsafe.Pointer {
a := &Animal{
Name: name,
}
return noescape(unsafe.Pointer(a))
}
func main() {
a := (*Animal)(NewAnimal("dog"))
fmt.Println(a)
}
编译结果:
go build -gcflags '-m -l' ./main.go
./main.go:8:15: p does not escape
./main.go:17:16: name does not escape
./main.go:18:7: &Animal{...} does not escape
./main.go:27:13: ... argument does not escape
5. 总结
- 逃逸分析是编译器在静态编译时完成的
- 逃逸分析可以确定哪些变量分配在栈上,栈的性能更好
参考资料:https://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651449591&idx=3&sn=c5eba0fc5cd4168565830d0a3c5436fb&chksm=80bb3605b7ccbf1352965e5cf2e577ce59732426dff2b0b4e5fb4d48c52c2d88d009b39d8476&scene=21#wechat_redirect