1.使用go提供的指令
package main
import "fmt"
func func() *int {
a := 3
return &a
}
func main() {
b := func()
fmt.Println(*b)
}
执行编译指令:go build -gcflags ‘-m -l’ main.go
- -gcflags 用于请用编译器支持的额外标志;
- -m 用于输出编译器优化细节(包括使用逃逸分析这种优化);
- -N 用来关闭编译器优化;
- -l 用于禁用函数的内联优化,防止逃逸编译器通过内联彻底的抹除;
输出结果:
# command-line-arguments
./main.go:6: moved to heap: a
./main.go:7: &a escapes to heap
./main.go:13: *b escapes to heap
./main.go:13: main ... argument does not escape
我们来分析一下结果,很明显,变量a发生了逃逸,被分配到堆内存。但是有一点大家可能不明白main函数中的b也发生了逃逸,这是因为fmt.Println(a …interface{})参数是interface,编译期间很难确定其具体类型,这种情况也会发生逃逸。
2.使用反汇编命令
go tool compile -S main.go
终端输出结果:
"".foo t=1 size=64 value=0 args=0x8 locals=0x10
0x0000 00000 (main.go:5) TEXT "".foo(SB), $16-8
0x0000 00000 (main.go:5) MOVQ (TLS), CX
0x0009 00009 (main.go:5) CMPQ SP, 16(CX)
0x000d 00013 (main.go:5) JLS 57
0x000f 00015 (main.go:5) SUBQ $16, SP
0x0013 00019 (main.go:5) FUNCDATA $0, gclocals·5184031d3a32a42d85027f073f873668(SB)
0x0013 00019 (main.go:5) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0013 00019 (main.go:6) LEAQ type.int(SB), BX
0x001a 00026 (main.go:6) MOVQ BX, (SP)
0x001e 00030 (main.go:6) PCDATA $0, $0
0x001e 00030 (main.go:6) CALL runtime.newobject(SB)
0x0023 00035 (main.go:6) MOVQ 8(SP), AX
0x0028 00040 (main.go:6) MOVQ $3, (AX)
0x002f 00047 (main.go:7) MOVQ AX, "".~r0+24(FP)
0x0034 00052 (main.go:7) ADDQ $16, SP
0x0038 00056 (main.go:7) RET
0x0039 00057 (main.go:7) NOP
0x0039 00057 (main.go:5) CALL runtime.morestack_noctxt(SB)
0x003e 00062 (main.go:5) JMP 0
0x0000 64 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 2a 48 dH..%....H;a.v*H
0x0010 83 ec 10 48 8d 1d 00 00 00 00 48 89 1c 24 e8 00 ...H......H..$..
0x0020 00 00 00 48 8b 44 24 08 48 c7 00 03 00 00 00 48 ...H.D$.H......H
0x0030 89 44 24 18 48 83 c4 10 c3 e8 00 00 00 00 eb c0 .D$.H...........
// 因为输出的比较多,我们值取重要的部分,其他部分省略
结果分析,0x001e 00030 (main.go:6) CALL runtime.newobject(SB) 用于分配内存到堆上,说明a被存放到堆上了,也就是发生了逃逸。