如何确定是否发生逃逸
Go 提供了相关的命令,可以查看变量是否发生逃逸。使用前面提到的例子:
package main
import "fmt"
func main() {
x := foo()
fmt.Println(*x)
}
func foo() *int {
t := 3
return &t
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
foo 函数返回一个局部变量的指针,使用 main 函数里变量 x 接收它。执行如下命令:
go build -gcflags '-m -l' main.go
1
其中 -gcflags 参数用于启用编译器支持的额外标志。例如,-m 用于输出编译器的优化细节(包括使用逃逸分析这种优化),相反可以使用 -N 来关闭编译器优化;而 -l 则用于禁用 foo 函数的内联优化,防止逃逸被编译器通过内联彻底的抹除。得到如下输出:
# command-line-arguments
./main.go:11:2: moved to heap: t
./main.go:7:13: ... argument does not escape
./main.go:7:14: *x escapes to heap
1
2
3
4
2
3
4
foo 函数里的变量 t 逃逸了,和预想的一致,不解的是为什么 main 函数里的 x 也逃逸了? 这是因为有些函数的参数为 interface 类型,比如 fmt.Println (a ...interface {}),编译期间很难确定其参数的具体类型,也会发生逃逸。
使用反汇编命令也可以看出变量是否发生逃逸。执行命令:
截取部分结果如图 1-1 所示,图中标记出来的函数 newobject 用于在堆上分配一块内存,从而说明 t 被存放到了堆上,也就是发生了逃逸。
上次更新: 5/9/2023, 10:58:32 AM