Golang(十二)[逃逸分析]

逃逸分析: 通过编译参数 go build -gcflags ‘-m’ xxx.go 可以查看编译过程中的逃逸分析
参考文档:书栈网-《GO专家编程》【逃逸分析】-Hongcai Ren
环境配置:
Windows 10 x64 专业版
Golang 1.14.1
GO111MODULE=on
IDE : VsCode
在这里插入图片描述
打开Vscode,打开对一个工程目录,并做go mod 初始化
在这里插入图片描述

1.概述

逃逸分析(Escape analysis)是指由编译器(golang编译器)决定内存分配的位置,不需要程序员指定。函数中申请一个新的对象:

  • 如果分配在栈中,则函数执行结束可自动将内存回收;
  • 如果分配在堆中,则函数执行结束可交给GC(垃圾回收)处理;

有了逃逸分析,返回函数局部变量将变得可能,除此之外,逃逸分析还跟闭包息息相关,了解哪些场景下对象会逃逸至关重要。

2.逃逸策略

每当函数中申请新的对象,golang编译器会跟据该对象是否被函数外部引用来决定是否逃逸:

  1. 如果函数外部没有引用,则优先放到栈中;
  2. 如果函数外部存在引用,则必定放到堆中;

注意:
对于函数外部没有引用的对象,也有可能放到堆中,比如内存过大超过栈的存储能力。

3.逃逸确定

编译阶段

4.逃逸场景

1.指针逃逸

package main

type Student struct {
	Name string
	Age  int
}

func GetStudent() *Student {
	stu := &Student{Name: "Lisa", Age: 18} // 局部指针变量stu逃逸到了堆中
	return stu
}

func main() {
	GetStudent()
}

编译命令:

go build -gcflags '-m' .\pointer_escape.go

指针变量逃逸到堆中
反汇编:

go tool compile -S .\pointer_escape.go

反编译导出

go tool compile -S .\pointer_escape.go > .\pointer_escape.s
"".GetStudent STEXT size=105 args=0x8 locals=0x18
	0x0000 00000 (.\pointer_escape.go:8)	TEXT	"".GetStudent(SB), ABIInternal, $24-8
	0x0000 00000 (.\pointer_escape.go:8)	MOVQ	TLS, CX
	0x0009 00009 (.\pointer_escape.go:8)	PCDATA	$0, $-2
	0x0009 00009 (.\pointer_escape.go:8)	MOVQ	(CX)(TLS*2), CX
	0x0010 00016 (.\pointer_escape.go:8)	PCDATA	$0, $-1
	0x0010 00016 (.\pointer_escape.go:8)	CMPQ	SP, 16(CX)
	0x0014 00020 (.\pointer_escape.go:8)	PCDATA	$0, $-2
	0x0014 00020 (.\pointer_escape.go:8)	JLS	98
	0x0016 00022 (.\pointer_escape.go:8)	PCDATA	$0, $-1
	0x0016 00022 (.\pointer_escape.go:8)	SUBQ	$24, SP
	0x001a 00026 (.\pointer_escape.go:8)	MOVQ	BP, 16(SP)
	0x001f 00031 (.\pointer_escape.go:8)	LEAQ	16(SP), BP
	0x0024 00036 (.\pointer_escape.go:8)	PCDATA	$0, $-2
	0x0024 00036 (.\pointer_escape.go:8)	PCDATA	$1, $-2
	0x0024 00036 (.\pointer_escape.go:8)	FUNCDATA	$0, gclocals路9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x0024 00036 (.\pointer_escape.go:8)	FUNCDATA	$1, gclocals路69c1753bd5f81501d95132d08af04464(SB)
	0x0024 00036 (.\pointer_escape.go:8)	FUNCDATA	$2, gclocals路bfec7e55b3f043d1941c093912808913(SB)
	0x0024 00036 (.\pointer_escape.go:9)	PCDATA	$0, $1
	0x0024 00036 (.\pointer_escape.go:9)	PCDATA	$1, $0
	0x0024 00036 (.\pointer_escape.go:9)	LEAQ	type."".Student(SB), AX
	0x002b 00043 (.\pointer_escape.go:9)	PCDATA	$0, $0
	0x002b 00043 (.\pointer_escape.go:9)	MOVQ	AX, (SP)
	0x002f 00047 (.\pointer_escape.go:9)	CALL	runtime.newobject(SB)
	0x0034 00052 (.\pointer_escape.go:9)	PCDATA	$0, $1
	0x0034 00052 (.\pointer_escape.go:9)	MOVQ	8(SP), AX
	0x0039 00057 (.\pointer_escape.go:9)	MOVQ	$4, 8(AX)
	0x0041 00065 (.\pointer_escape.go:9)	PCDATA	$0, $2
	0x0041 00065 (.\pointer_escape.go:9)	LEAQ	go.string."Lisa"(SB), CX
	0x0048 00072 (.\pointer_escape.go:9)	PCDATA	$0, $1
	0x0048 00072 (.\pointer_escape.go:9)	MOVQ	CX, (AX)
	0x004b 00075 (.\pointer_escape.go:9)	MOVQ	$18, 16(AX)
	0x0053 00083 (.\pointer_escape.go:10)	PCDATA	$0, $0
	0x0053 00083 (.\pointer_escape.go:10)	PCDATA	$1, $1
	0x0053 00083 (.\pointer_escape.go:10)	MOVQ	AX, "".~r0+32(SP)
	0x0058 00088 (.\pointer_escape.go:10)	MOVQ	16(SP), BP
	0x005d 00093 (.\pointer_escape.go:10)	ADDQ	$24, SP
	0x0061 00097 (.\pointer_escape.go:10)	RET
	0x0062 00098 (.\pointer_escape.go:10)	NOP
	0x0062 00098 (.\pointer_escape.go:8)	PCDATA	$1, $-1
	0x0062 00098 (.\pointer_escape.go:8)	PCDATA	$0, $-2
	0x0062 00098 (.\pointer_escape.go:8)	CALL	runtime.morestack_noctxt(SB)
	0x0067 00103 (.\pointer_escape.go:8)	PCDATA	$0, $-1
	0x0067 00103 (.\pointer_escape.go:8)	JMP	0
	0x0000 65 48 8b 0c 25 28 00 00 00 48 8b 89 00 00 00 00  eH..%(...H......
	0x0010 48 3b 61 10 76 4c 48 83 ec 18 48 89 6c 24 10 48  H;a.vLH...H.l$.H
	0x0020 8d 6c 24 10 48 8d 05 00 00 00 00 48 89 04 24 e8  .l$.H......H..$.
	0x0030 00 00 00 00 48 8b 44 24 08 48 c7 40 08 04 00 00  ....H.D$.H.@....
	0x0040 00 48 8d 0d 00 00 00 00 48 89 08 48 c7 40 10 12  .H......H..H.@..
	0x0050 00 00 00 48 89 44 24 20 48 8b 6c 24 10 48 83 c4  ...H.D$ H.l$.H..
	0x0060 18 c3 e8 00 00 00 00 eb 97                       .........
	rel 12+4 t=17 TLS+0
	rel 39+4 t=16 type."".Student+0
	rel 48+4 t=8 runtime.newobject+0
	rel 68+4 t=16 go.string."Lisa"+0
	rel 99+4 t=8 runtime.morestack_noctxt+0
"".main STEXT nosplit size=1 args=0x0 locals=0x0
	0x0000 00000 (.\pointer_escape.go:13)	TEXT	"".main(SB), NOSPLIT|ABIInternal, $0-0
	0x0000 00000 (.\pointer_escape.go:13)	PCDATA	$0, $-2
	0x0000 00000 (.\pointer_escape.go:13)	PCDATA	$1, $-2
	0x0000 00000 (.\pointer_escape.go:13)	FUNCDATA	$0, gclocals路33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (.\pointer_escape.go:13)	FUNCDATA	$1, gclocals路33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (.\pointer_escape.go:13)	FUNCDATA	$2, gclocals路33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (.\pointer_escape.go:14)	PCDATA	$0, $-1
	0x0000 00000 (.\pointer_escape.go:14)	PCDATA	$1, $-1
	0x0000 00000 (.\pointer_escape.go:14)	RET
	0x0000 c3                                               .
type..eq."".Student STEXT dupok size=143 args=0x18 locals=0x28
	0x0000 00000 (<autogenerated>:1)	TEXT	type..eq."".Student(SB), DUPOK|ABIInternal, $40-24
	0x0000 00000 (<autogenerated>:1)	MOVQ	TLS, CX
	0x0009 00009 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0009 00009 (<autogenerated>:1)	MOVQ	(CX)(TLS*2), CX
	0x0010 00016 (<autogenerated>:1)	PCDATA	$0, $-1
	0x0010 00016 (<autogenerated>:1)	CMPQ	SP, 16(CX)
	0x0014 00020 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0014 00020 (<autogenerated>:1)	JLS	133
	0x0016 00022 (<autogenerated>:1)	PCDATA	$0, $-1
	0x0016 00022 (<autogenerated>:1)	SUBQ	$40, SP
	0x001a 00026 (<autogenerated>:1)	MOVQ	BP, 32(SP)
	0x001f 00031 (<autogenerated>:1)	LEAQ	32(SP), BP
	0x0024 00036 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0024 00036 (<autogenerated>:1)	PCDATA	$1, $-2
	0x0024 00036 (<autogenerated>:1)	FUNCDATA	$0, gclocals路ef6c193a450e4116e290c9970add59e0(SB)
	0x0024 00036 (<autogenerated>:1)	FUNCDATA	$1, gclocals路7d2d5fca80364273fb07d5820a76fef4(SB)
	0x0024 00036 (<autogenerated>:1)	FUNCDATA	$2, gclocals路983e15b167a5ba7781b4fa16563d809c(SB)
	0x0024 00036 (<autogenerated>:1)	PCDATA	$0, $1
	0x0024 00036 (<autogenerated>:1)	PCDATA	$1, $0
	0x0024 00036 (<autogenerated>:1)	MOVQ	"".p+48(SP), AX
	0x0029 00041 (<autogenerated>:1)	MOVQ	8(AX), CX
	0x002d 00045 (<autogenerated>:1)	PCDATA	$0, $2
	0x002d 00045 (<autogenerated>:1)	MOVQ	(AX), DX
	0x0030 00048 (<autogenerated>:1)	PCDATA	$0, $3
	0x0030 00048 (<autogenerated>:1)	MOVQ	"".q+56(SP), BX
	0x0035 00053 (<autogenerated>:1)	PCDATA	$0, $4
	0x0035 00053 (<autogenerated>:1)	MOVQ	(BX), SI
	0x0038 00056 (<autogenerated>:1)	CMPQ	8(BX), CX
	0x003c 00060 (<autogenerated>:1)	JEQ	97
	0x003e 00062 (<autogenerated>:1)	PCDATA	$0, $5
	0x003e 00062 (<autogenerated>:1)	PCDATA	$1, $1
	0x003e 00062 (<autogenerated>:1)	XORL	CX, CX
	0x0040 00064 (<autogenerated>:1)	TESTB	CL, CL
	0x0042 00066 (<autogenerated>:1)	JEQ	93
	0x0044 00068 (<autogenerated>:1)	PCDATA	$0, $1
	0x0044 00068 (<autogenerated>:1)	MOVQ	16(BX), CX
	0x0048 00072 (<autogenerated>:1)	PCDATA	$0, $0
	0x0048 00072 (<autogenerated>:1)	CMPQ	16(AX), CX
	0x004c 00076 (<autogenerated>:1)	SETEQ	AL
	0x004f 00079 (<autogenerated>:1)	MOVB	AL, "".~r2+64(SP)
	0x0053 00083 (<autogenerated>:1)	MOVQ	32(SP), BP
	0x0058 00088 (<autogenerated>:1)	ADDQ	$40, SP
	0x005c 00092 (<autogenerated>:1)	RET
	0x005d 00093 (<autogenerated>:1)	XORL	AX, AX
	0x005f 00095 (<autogenerated>:1)	JMP	79
	0x0061 00097 (<autogenerated>:1)	PCDATA	$0, $6
	0x0061 00097 (<autogenerated>:1)	PCDATA	$1, $0
	0x0061 00097 (<autogenerated>:1)	MOVQ	DX, (SP)
	0x0065 00101 (<autogenerated>:1)	PCDATA	$0, $0
	0x0065 00101 (<autogenerated>:1)	MOVQ	SI, 8(SP)
	0x006a 00106 (<autogenerated>:1)	MOVQ	CX, 16(SP)
	0x006f 00111 (<autogenerated>:1)	CALL	runtime.memequal(SB)
	0x0074 00116 (<autogenerated>:1)	MOVBLZX	24(SP), CX
	0x0079 00121 (<autogenerated>:1)	PCDATA	$0, $1
	0x0079 00121 (<autogenerated>:1)	PCDATA	$1, $2
	0x0079 00121 (<autogenerated>:1)	MOVQ	"".p+48(SP), AX
	0x007e 00126 (<autogenerated>:1)	PCDATA	$0, $5
	0x007e 00126 (<autogenerated>:1)	PCDATA	$1, $1
	0x007e 00126 (<autogenerated>:1)	MOVQ	"".q+56(SP), BX
	0x0083 00131 (<autogenerated>:1)	JMP	64
	0x0085 00133 (<autogenerated>:1)	NOP
	0x0085 00133 (<autogenerated>:1)	PCDATA	$1, $-1
	0x0085 00133 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0085 00133 (<autogenerated>:1)	CALL	runtime.morestack_noctxt(SB)
	0x008a 00138 (<autogenerated>:1)	PCDATA	$0, $-1
	0x008a 00138 (<autogenerated>:1)	JMP	0
	0x0000 65 48 8b 0c 25 28 00 00 00 48 8b 89 00 00 00 00  eH..%(...H......
	0x0010 48 3b 61 10 76 6f 48 83 ec 28 48 89 6c 24 20 48  H;a.voH..(H.l$ H
	0x0020 8d 6c 24 20 48 8b 44 24 30 48 8b 48 08 48 8b 10  .l$ H.D$0H.H.H..
	0x0030 48 8b 5c 24 38 48 8b 33 48 39 4b 08 74 23 31 c9  H.\$8H.3H9K.t#1.
	0x0040 84 c9 74 19 48 8b 4b 10 48 39 48 10 0f 94 c0 88  ..t.H.K.H9H.....
	0x0050 44 24 40 48 8b 6c 24 20 48 83 c4 28 c3 31 c0 eb  [email protected]$ H..(.1..
	0x0060 ee 48 89 14 24 48 89 74 24 08 48 89 4c 24 10 e8  .H..$H.t$.H.L$..
	0x0070 00 00 00 00 0f b6 4c 24 18 48 8b 44 24 30 48 8b  ......L$.H.D$0H.
	0x0080 5c 24 38 eb bb e8 00 00 00 00 e9 71 ff ff ff     \$8........q...
	rel 12+4 t=17 TLS+0
	rel 112+4 t=8 runtime.memequal+0
	rel 134+4 t=8 runtime.morestack_noctxt+0
go.cuinfo.packagename. SDWARFINFO dupok size=0
	0x0000 6d 61 69 6e                                      main
go.info."".GetStudent$abstract SDWARFINFO dupok size=26
	0x0000 04 2e 47 65 74 53 74 75 64 65 6e 74 00 01 01 0c  ..GetStudent....
	0x0010 73 74 75 00 09 00 00 00 00 00                    stu.......
	rel 21+4 t=29 go.info.*"".Student+0
go.string."Lisa" SRODATA dupok size=4
	0x0000 4c 69 73 61                                      Lisa
go.loc."".GetStudent SDWARFLOC size=51
	0x0000 ff ff ff ff ff ff ff ff 00 00 00 00 00 00 00 00  ................
	0x0010 39 00 00 00 00 00 00 00 69 00 00 00 00 00 00 00  9.......i.......
	0x0020 01 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00  ..P.............
	0x0030 00 00 00                                         ...
	rel 8+8 t=1 "".GetStudent+0
go.info."".GetStudent SDWARFINFO size=45
	0x0000 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0010 00 00 00 00 00 01 9c 0f 7e 72 30 00 01 08 00 00  ........~r0.....
	0x0020 00 00 00 0e 00 00 00 00 00 00 00 00 00           .............
	rel 0+0 t=24 type.*"".Student+0
	rel 1+4 t=29 go.info."".GetStudent$abstract+0
	rel 5+8 t=1 "".GetStudent+0
	rel 13+8 t=1 "".GetStudent+105
	rel 30+4 t=29 go.info.*"".Student+0
	rel 36+4 t=29 go.info."".GetStudent$abstract+15
	rel 40+4 t=29 go.loc."".GetStudent+0
go.range."".GetStudent SDWARFRANGE size=0
go.debuglines."".GetStudent SDWARFMISC size=22
	0x0000 04 02 03 02 14 0a eb 9c 06 55 06 08 b0 06 41 06  .........U....A.
	0x0010 71 04 01 03 79 01                                q...y.
go.loc."".main SDWARFLOC size=0
go.info."".main SDWARFINFO size=33
	0x0000 03 22 22 2e 6d 61 69 6e 00 00 00 00 00 00 00 00  ."".main........
	0x0010 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 00 01  ................
	0x0020 00                                               .
	rel 9+8 t=1 "".main+0
	rel 17+8 t=1 "".main+1
	rel 27+4 t=30 gofile..D:\ProgramData\Workspace\VSCode\Code\pointer_escape.go+0
go.range."".main SDWARFRANGE size=0
go.debuglines."".main SDWARFMISC size=10
	0x0000 04 02 03 08 14 04 01 03 73 01                    ........s.
go.loc.type..eq."".Student SDWARFLOC dupok size=103
	0x0000 ff ff ff ff ff ff ff ff 00 00 00 00 00 00 00 00  ................
	0x0010 00 00 00 00 00 00 00 00 8f 00 00 00 00 00 00 00  ................
	0x0020 01 00 9c 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 00  ................
	0x0040 00 00 00 00 00 00 00 00 00 00 00 8f 00 00 00 00  ................
	0x0050 00 00 00 02 00 91 08 00 00 00 00 00 00 00 00 00  ................
	0x0060 00 00 00 00 00 00 00                             .......
	rel 8+8 t=1 type..eq."".Student+0
	rel 59+8 t=1 type..eq."".Student+0
go.info.type..eq."".Student SDWARFINFO dupok size=83
	0x0000 03 74 79 70 65 2e 2e 65 71 2e 22 22 2e 53 74 75  .type..eq."".Stu
	0x0010 64 65 6e 74 00 00 00 00 00 00 00 00 00 00 00 00  dent............
	0x0020 00 00 00 00 00 01 9c 00 00 00 00 01 10 70 00 00  .............p..
	0x0030 01 00 00 00 00 00 00 00 00 10 71 00 00 01 00 00  ..........q.....
	0x0040 00 00 00 00 00 00 0f 7e 72 32 00 01 01 00 00 00  .......~r2......
	0x0050 00 00 00                                         ...
	rel 0+0 t=24 type.*"".Student+0
	rel 0+0 t=24 type.bool+0
	rel 21+8 t=1 type..eq."".Student+0
	rel 29+8 t=1 type..eq."".Student+143
	rel 39+4 t=30 gofile..<autogenerated>+0
	rel 49+4 t=29 go.info.*"".Student+0
	rel 53+4 t=29 go.loc.type..eq."".Student+0
	rel 62+4 t=29 go.info.*"".Student+0
	rel 66+4 t=29 go.loc.type..eq."".Student+51
	rel 77+4 t=29 go.info.bool+0
go.range.type..eq."".Student SDWARFRANGE dupok size=0
go.debuglines.type..eq."".Student SDWARFMISC dupok size=21
	0x0000 04 01 0f 0a eb 06 cd 06 08 9b 06 37 06 02 1a ff  ...........7....
	0x0010 04 01 03 00 01                                   .....
type..eqfunc."".Student SRODATA dupok size=8
	0x0000 00 00 00 00 00 00 00 00                          ........
	rel 0+8 t=1 type..eq."".Student+0
runtime.memequal64路f SRODATA dupok size=8
	0x0000 00 00 00 00 00 00 00 00                          ........
	rel 0+8 t=1 runtime.memequal64+0
runtime.gcbits.01 SRODATA dupok size=1
	0x0000 01                                               .
type..namedata.*main.Student. SRODATA dupok size=16
	0x0000 01 00 0d 2a 6d 61 69 6e 2e 53 74 75 64 65 6e 74  ...*main.Student
type.*"".Student SRODATA size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 0c 31 79 12 08 08 08 36 00 00 00 00 00 00 00 00  .1y....6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.memequal64路f+0
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*main.Student.+0
	rel 48+8 t=1 type."".Student+0
type..namedata.Name. SRODATA dupok size=7
	0x0000 01 00 04 4e 61 6d 65                             ...Name
type..namedata.Age. SRODATA dupok size=6
	0x0000 01 00 03 41 67 65                                ...Age
type."".Student SRODATA size=144
	0x0000 18 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 da 9f 20 d4 07 08 08 19 00 00 00 00 00 00 00 00  .. .............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0040 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
	0x0050 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  ........@.......
	0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0080 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00  ........ .......
	rel 24+8 t=1 type..eqfunc."".Student+0
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*main.Student.+0
	rel 44+4 t=5 type.*"".Student+0
	rel 56+8 t=1 type."".Student+96
	rel 80+4 t=5 type..importpath."".+0
	rel 96+8 t=1 type..namedata.Name.+0
	rel 104+8 t=1 type.string+0
	rel 120+8 t=1 type..namedata.Age.+0
	rel 128+8 t=1 type.int+0
""..inittask SNOPTRDATA size=24
	0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0010 00 00 00 00 00 00 00 00                          ........
gclocals路9fb7f0986f647f17cb53dda1484e0f7a SRODATA dupok size=10
	0x0000 02 00 00 00 01 00 00 00 00 01                    ..........
gclocals路69c1753bd5f81501d95132d08af04464 SRODATA dupok size=8
	0x0000 02 00 00 00 00 00 00 00                          ........
gclocals路bfec7e55b3f043d1941c093912808913 SRODATA dupok size=11
	0x0000 03 00 00 00 02 00 00 00 00 01 03                 ...........
gclocals路33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
	0x0000 01 00 00 00 00 00 00 00                          ........
"".main.stkobj SRODATA size=0
gclocals路ef6c193a450e4116e290c9970add59e0 SRODATA dupok size=11
	0x0000 03 00 00 00 02 00 00 00 03 00 02                 ...........
gclocals路7d2d5fca80364273fb07d5820a76fef4 SRODATA dupok size=8
	0x0000 03 00 00 00 00 00 00 00                          ........
gclocals路983e15b167a5ba7781b4fa16563d809c SRODATA dupok size=15
	0x0000 07 00 00 00 06 00 00 00 00 01 05 0d 2d 09 20     ............-. 

汇编显示确实分配到了堆中

1.思考

1.是否意味着所有的指针对象都会被分配到堆上?
2.是否意味着所有的指针对象都会发生变量逃逸?

代码修改如下:

package main

type Student struct {
	Name string
	Age  int
}

func GetStudent() {
	stu := &Student{}
	stu.Name = "Lisa"
	stu.Age = 18
}

func main() {
	GetStudent()
}

没有发生逃逸

2.结论

1.在GetStudent( )方法中,GetStudent( ) 返回的是指针对象,指针对象返回GetStudent( )方法之外,被main( )方法引用。因此编译器会把该对象分配到堆上,而不是栈上。否则GetStudent( )方法结束之后,局部变量就被回收了。所以最终分配到堆上。
2.在修改后的代码中,指针对象在GetStudent( )方法之中,没有被main( )方法引用。因此编译器会把该对象分配到栈上,没有发生逃逸事件。GetStudent( )方法结束之后,局部指针变量stu就被回收。

2.动态(interface)类型逃逸

在go语言中,所有的类型都实现了动态类型【interface】是一种不确定类型。

package main

func  main()  {
	var i int
	i = 5
}

编译命令:

go build -gcflags '-m' .\escape.go

并未发现逃逸事件
修改代码如下:

package main

import "fmt"

func  main()  {
	var i int
	i = 5
	fmt.Println(i)
}

修改后再次执行编译:

go build -gcflags '-m' .\escape.go

发生了逃逸

1.思考

为什么没有fmt.println(i)没有发生逃逸事件,反而添加了就发生了逃逸事件?
反编译导出:

go tool compile -S .\escape.go >escape.s
os.(*File).close STEXT dupok nosplit size=26 args=0x18 locals=0x0
	0x0000 00000 (<autogenerated>:1)	TEXT	os.(*File).close(SB), DUPOK|NOSPLIT|ABIInternal, $0-24
	0x0000 00000 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0000 00000 (<autogenerated>:1)	PCDATA	$1, $-2
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$0, gclocals路e6397a44f8e1b6e77d0f200b4fba5269(SB)
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$1, gclocals路69c1753bd5f81501d95132d08af04464(SB)
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$2, gclocals路9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x0000 00000 (<autogenerated>:1)	PCDATA	$0, $1
	0x0000 00000 (<autogenerated>:1)	PCDATA	$1, $1
	0x0000 00000 (<autogenerated>:1)	MOVQ	""..this+8(SP), AX
	0x0005 00005 (<autogenerated>:1)	MOVQ	(AX), AX
	0x0008 00008 (<autogenerated>:1)	PCDATA	$0, $0
	0x0008 00008 (<autogenerated>:1)	PCDATA	$1, $0
	0x0008 00008 (<autogenerated>:1)	MOVQ	AX, ""..this+8(SP)
	0x000d 00013 (<autogenerated>:1)	XORPS	X0, X0
	0x0010 00016 (<autogenerated>:1)	MOVUPS	X0, "".~r0+16(SP)
	0x0015 00021 (<autogenerated>:1)	JMP	os.(*file).close(SB)
	0x0000 48 8b 44 24 08 48 8b 00 48 89 44 24 08 0f 57 c0  H.D$.H..H.D$..W.
	0x0010 0f 11 44 24 10 e9 00 00 00 00                    ..D$......
	rel 22+4 t=8 os.(*file).close+0
os.(*File).isdir STEXT dupok nosplit size=23 args=0x10 locals=0x0
	0x0000 00000 (<autogenerated>:1)	TEXT	os.(*File).isdir(SB), DUPOK|NOSPLIT|ABIInternal, $0-16
	0x0000 00000 (<autogenerated>:1)	PCDATA	$0, $-2
	0x0000 00000 (<autogenerated>:1)	PCDATA	$1, $-2
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$0, gclocals路1a65e721a2ccc325b382662e7ffee780(SB)
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$1, gclocals路69c1753bd5f81501d95132d08af04464(SB)
	0x0000 00000 (<autogenerated>:1)	FUNCDATA	$2, gclocals路9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x0000 00000 (<autogenerated>:1)	PCDATA	$0, $1
	0x0000 00000 (<autogenerated>:1)	PCDATA	$1, $1
	0x0000 00000 (<autogenerated>:1)	MOVQ	""..this+8(SP), AX
	0x0005 00005 (<autogenerated>:1)	MOVQ	(AX), AX
	0x0008 00008 (<autogenerated>:1)	PCDATA	$0, $0
	0x0008 00008 (<autogenerated>:1)	PCDATA	$1, $0
	0x0008 00008 (<autogenerated>:1)	MOVQ	AX, ""..this+8(SP)
	0x000d 00013 (<autogenerated>:1)	MOVB	$0, "".~r0+16(SP)
	0x0012 00018 (<autogenerated>:1)	JMP	os.(*file).isdir(SB)
	0x0000 48 8b 44 24 08 48 8b 00 48 89 44 24 08 c6 44 24  H.D$.H..H.D$..D$
	0x0010 10 00 e9 00 00 00 00                             .......
	rel 19+4 t=8 os.(*file).isdir+0
"".main STEXT size=155 args=0x0 locals=0x58
	0x0000 00000 (.\escape.go:5)	TEXT	"".main(SB), ABIInternal, $88-0
	0x0000 00000 (.\escape.go:5)	MOVQ	TLS, CX
	0x0009 00009 (.\escape.go:5)	PCDATA	$0, $-2
	0x0009 00009 (.\escape.go:5)	MOVQ	(CX)(TLS*2), CX
	0x0010 00016 (.\escape.go:5)	PCDATA	$0, $-1
	0x0010 00016 (.\escape.go:5)	CMPQ	SP, 16(CX)
	0x0014 00020 (.\escape.go:5)	PCDATA	$0, $-2
	0x0014 00020 (.\escape.go:5)	JLS	145
	0x0016 00022 (.\escape.go:5)	PCDATA	$0, $-1
	0x0016 00022 (.\escape.go:5)	SUBQ	$88, SP
	0x001a 00026 (.\escape.go:5)	MOVQ	BP, 80(SP)
	0x001f 00031 (.\escape.go:5)	LEAQ	80(SP), BP
	0x0024 00036 (.\escape.go:5)	PCDATA	$0, $-2
	0x0024 00036 (.\escape.go:5)	PCDATA	$1, $-2
	0x0024 00036 (.\escape.go:5)	FUNCDATA	$0, gclocals路69c1753bd5f81501d95132d08af04464(SB)
	0x0024 00036 (.\escape.go:5)	FUNCDATA	$1, gclocals路568470801006e5c0dc3947ea998fe279(SB)
	0x0024 00036 (.\escape.go:5)	FUNCDATA	$2, gclocals路bfec7e55b3f043d1941c093912808913(SB)
	0x0024 00036 (.\escape.go:5)	FUNCDATA	$3, "".main.stkobj(SB)
	0x0024 00036 (.\escape.go:8)	PCDATA	$0, $0
	0x0024 00036 (.\escape.go:8)	PCDATA	$1, $0
	0x0024 00036 (.\escape.go:8)	MOVQ	$5, (SP)
	0x002c 00044 (.\escape.go:8)	CALL	runtime.convT64(SB)
	0x0031 00049 (.\escape.go:8)	PCDATA	$0, $1
	0x0031 00049 (.\escape.go:8)	MOVQ	8(SP), AX
	0x0036 00054 (.\escape.go:8)	PCDATA	$1, $1
	0x0036 00054 (.\escape.go:8)	XORPS	X0, X0
	0x0039 00057 (.\escape.go:8)	MOVUPS	X0, ""..autotmp_13+64(SP)
	0x003e 00062 (.\escape.go:8)	PCDATA	$0, $2
	0x003e 00062 (.\escape.go:8)	LEAQ	type.int(SB), CX
	0x0045 00069 (.\escape.go:8)	PCDATA	$0, $1
	0x0045 00069 (.\escape.go:8)	MOVQ	CX, ""..autotmp_13+64(SP)
	0x004a 00074 (.\escape.go:8)	PCDATA	$0, $0
	0x004a 00074 (.\escape.go:8)	MOVQ	AX, ""..autotmp_13+72(SP)
	0x004f 00079 (<unknown line number>)	NOP
	0x004f 00079 ($GOROOT\src\fmt\print.go:274)	PCDATA	$0, $1
	0x004f 00079 ($GOROOT\src\fmt\print.go:274)	MOVQ	os.Stdout(SB), AX
	0x0056 00086 ($GOROOT\src\fmt\print.go:274)	PCDATA	$0, $2
	0x0056 00086 ($GOROOT\src\fmt\print.go:274)	LEAQ	go.itab.*os.File,io.Writer(SB), CX
	0x005d 00093 ($GOROOT\src\fmt\print.go:274)	PCDATA	$0, $1
	0x005d 00093 ($GOROOT\src\fmt\print.go:274)	MOVQ	CX, (SP)
	0x0061 00097 ($GOROOT\src\fmt\print.go:274)	PCDATA	$0, $0
	0x0061 00097 ($GOROOT\src\fmt\print.go:274)	MOVQ	AX, 8(SP)
	0x0066 00102 ($GOROOT\src\fmt\print.go:274)	PCDATA	$0, $1
	0x0066 00102 ($GOROOT\src\fmt\print.go:274)	PCDATA	$1, $0
	0x0066 00102 ($GOROOT\src\fmt\print.go:274)	LEAQ	""..autotmp_13+64(SP), AX
	0x006b 00107 ($GOROOT\src\fmt\print.go:274)	PCDATA	$0, $0
	0x006b 00107 ($GOROOT\src\fmt\print.go:274)	MOVQ	AX, 16(SP)
	0x0070 00112 ($GOROOT\src\fmt\print.go:274)	MOVQ	$1, 24(SP)
	0x0079 00121 ($GOROOT\src\fmt\print.go:274)	MOVQ	$1, 32(SP)
	0x0082 00130 ($GOROOT\src\fmt\print.go:274)	CALL	fmt.Fprintln(SB)
	0x0087 00135 (.\escape.go:8)	MOVQ	80(SP), BP
	0x008c 00140 (.\escape.go:8)	ADDQ	$88, SP
	0x0090 00144 (.\escape.go:8)	RET
	0x0091 00145 (.\escape.go:8)	NOP
	0x0091 00145 (.\escape.go:5)	PCDATA	$1, $-1
	0x0091 00145 (.\escape.go:5)	PCDATA	$0, $-2
	0x0091 00145 (.\escape.go:5)	CALL	runtime.morestack_noctxt(SB)
	0x0096 00150 (.\escape.go:5)	PCDATA	$0, $-1
	0x0096 00150 (.\escape.go:5)	JMP	0
	0x0000 65 48 8b 0c 25 28 00 00 00 48 8b 89 00 00 00 00  eH..%(...H......
	0x0010 48 3b 61 10 76 7b 48 83 ec 58 48 89 6c 24 50 48  H;a.v{H..XH.l$PH
	0x0020 8d 6c 24 50 48 c7 04 24 05 00 00 00 e8 00 00 00  .l$PH..$........
	0x0030 00 48 8b 44 24 08 0f 57 c0 0f 11 44 24 40 48 8d  .H.D$..W...D$@H.
	0x0040 0d 00 00 00 00 48 89 4c 24 40 48 89 44 24 48 48  [email protected]$HH
	0x0050 8b 05 00 00 00 00 48 8d 0d 00 00 00 00 48 89 0c  ......H......H..
	0x0060 24 48 89 44 24 08 48 8d 44 24 40 48 89 44 24 10  [email protected]$.
	0x0070 48 c7 44 24 18 01 00 00 00 48 c7 44 24 20 01 00  H.D$.....H.D$ ..
	0x0080 00 00 e8 00 00 00 00 48 8b 6c 24 50 48 83 c4 58  .......H.l$PH..X
	0x0090 c3 e8 00 00 00 00 e9 65 ff ff ff                 .......e...
	rel 12+4 t=17 TLS+0
	rel 45+4 t=8 runtime.convT64+0
	rel 65+4 t=16 type.int+0
	rel 82+4 t=16 os.Stdout+0
	rel 89+4 t=16 go.itab.*os.File,io.Writer+0
	rel 131+4 t=8 fmt.Fprintln+0
	rel 146+4 t=8 runtime.morestack_noctxt+0
go.cuinfo.packagename. SDWARFINFO dupok size=0
	0x0000 6d 61 69 6e                                      main
go.info.fmt.Println$abstract SDWARFINFO dupok size=42
	0x0000 04 66 6d 74 2e 50 72 69 6e 74 6c 6e 00 01 01 11  .fmt.Println....
	0x0010 61 00 00 00 00 00 00 11 6e 00 01 00 00 00 00 11  a.......n.......
	0x0020 65 72 72 00 01 00 00 00 00 00                    err.......
	rel 0+0 t=24 type.[]interface {}+0
	rel 0+0 t=24 type.error+0
	rel 0+0 t=24 type.int+0
	rel 19+4 t=29 go.info.[]interface {}+0
	rel 27+4 t=29 go.info.int+0
	rel 37+4 t=29 go.info.error+0
go.loc.os.(*File).close SDWARFLOC dupok size=0
go.info.os.(*File).close SDWARFINFO dupok size=55
	0x0000 03 6f 73 2e 28 2a 46 69 6c 65 29 2e 63 6c 6f 73  .os.(*File).clos
	0x0010 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  e...............
	0x0020 00 00 01 9c 00 00 00 00 01 0f 7e 72 30 00 01 c4  ..........~r0...
	0x0030 01 00 00 00 00 00 00                             .......
	rel 0+0 t=24 type.*os.File+0
	rel 0+0 t=24 type.error+0
	rel 18+8 t=1 os.(*File).close+0
	rel 26+8 t=1 os.(*File).close+26
	rel 36+4 t=30 gofile..<autogenerated>+0
	rel 49+4 t=29 go.info.error+0
go.range.os.(*File).close SDWARFRANGE dupok size=0
go.debuglines.os.(*File).close SDWARFMISC dupok size=12
	0x0000 04 01 0f 06 41 06 af 04 01 03 00 01              ....A.......
go.loc.os.(*File).isdir SDWARFLOC dupok size=0
go.info.os.(*File).isdir SDWARFINFO dupok size=54
	0x0000 03 6f 73 2e 28 2a 46 69 6c 65 29 2e 69 73 64 69  .os.(*File).isdi
	0x0010 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  r...............
	0x0020 00 00 01 9c 00 00 00 00 01 0f 7e 72 30 00 01 63  ..........~r0..c
	0x0030 00 00 00 00 00 00                                ......
	rel 0+0 t=24 type.*os.File+0
	rel 0+0 t=24 type.bool+0
	rel 18+8 t=1 os.(*File).isdir+0
	rel 26+8 t=1 os.(*File).isdir+23
	rel 36+4 t=30 gofile..<autogenerated>+0
	rel 48+4 t=29 go.info.bool+0
go.range.os.(*File).isdir SDWARFRANGE dupok size=0
go.debuglines.os.(*File).isdir SDWARFMISC dupok size=12
	0x0000 04 01 0f 06 41 06 91 04 01 03 00 01              ....A.......
go.loc."".main SDWARFLOC size=0
go.info."".main SDWARFINFO size=66
	0x0000 03 22 22 2e 6d 61 69 6e 00 00 00 00 00 00 00 00  ."".main........
	0x0010 00 00 00 00 00 00 00 00 00 01 9c 00 00 00 00 01  ................
	0x0020 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00 00 08 12 00 00 00 00 00  ................
	0x0040 00 00                                            ..
	rel 0+0 t=24 type.[1]interface {}+0
	rel 9+8 t=1 "".main+0
	rel 17+8 t=1 "".main+155
	rel 27+4 t=30 gofile..D:\ProgramData\Workspace\VSCode\Code\escape.go+0
	rel 33+4 t=29 go.info.fmt.Println$abstract+0
	rel 37+8 t=1 "".main+79
	rel 45+8 t=1 "".main+135
	rel 53+4 t=30 gofile..D:\ProgramData\Workspace\VSCode\Code\escape.go+0
	rel 59+4 t=29 go.info.fmt.Println$abstract+15
go.range."".main SDWARFRANGE size=0
go.debuglines."".main SDWARFMISC size=33
	0x0000 04 02 13 0a eb 9e 06 5f 04 03 06 08 03 85 02 82  ......._........
	0x0010 06 55 04 02 06 02 19 03 fa 7d fb 70 04 01 03 7c  .U.......}.p...|
	0x0020 01                                               .
runtime.nilinterequal路f SRODATA dupok size=8
	0x0000 00 00 00 00 00 00 00 00                          ........
	rel 0+8 t=1 runtime.nilinterequal+0
runtime.memequal64路f SRODATA dupok size=8
	0x0000 00 00 00 00 00 00 00 00                          ........
	rel 0+8 t=1 runtime.memequal64+0
runtime.gcbits.01 SRODATA dupok size=1
	0x0000 01                                               .
type..namedata.*interface {}- SRODATA dupok size=16
	0x0000 00 00 0d 2a 69 6e 74 65 72 66 61 63 65 20 7b 7d  ...*interface {}
type.*interface {} SRODATA dupok size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 4f 0f 96 9d 08 08 08 36 00 00 00 00 00 00 00 00  O......6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.memequal64路f+0
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*interface {}-+0
	rel 48+8 t=1 type.interface {}+0
runtime.gcbits.02 SRODATA dupok size=1
	0x0000 02                                               .
type.interface {} SRODATA dupok size=80
	0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
	0x0010 e7 57 a0 18 02 08 08 14 00 00 00 00 00 00 00 00  .W..............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	rel 24+8 t=1 runtime.nilinterequal路f+0
	rel 32+8 t=1 runtime.gcbits.02+0
	rel 40+4 t=5 type..namedata.*interface {}-+0
	rel 44+4 t=6 type.*interface {}+0
	rel 56+8 t=1 type.interface {}+80
type..namedata.*[]interface {}- SRODATA dupok size=18
	0x0000 00 00 0f 2a 5b 5d 69 6e 74 65 72 66 61 63 65 20  ...*[]interface 
	0x0010 7b 7d                                            {}
type.*[]interface {} SRODATA dupok size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 f3 04 9a e7 08 08 08 36 00 00 00 00 00 00 00 00  .......6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.memequal64路f+0
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*[]interface {}-+0
	rel 48+8 t=1 type.[]interface {}+0
type.[]interface {} SRODATA dupok size=56
	0x0000 18 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 70 93 ea 2f 02 08 08 17 00 00 00 00 00 00 00 00  p../............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*[]interface {}-+0
	rel 44+4 t=6 type.*[]interface {}+0
	rel 48+8 t=1 type.interface {}+0
type..namedata.*[1]interface {}- SRODATA dupok size=19
	0x0000 00 00 10 2a 5b 31 5d 69 6e 74 65 72 66 61 63 65  ...*[1]interface
	0x0010 20 7b 7d                                          {}
type.*[1]interface {} SRODATA dupok size=56
	0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
	0x0010 bf 03 a8 35 08 08 08 36 00 00 00 00 00 00 00 00  ...5...6........
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.memequal64路f+0
	rel 32+8 t=1 runtime.gcbits.01+0
	rel 40+4 t=5 type..namedata.*[1]interface {}-+0
	rel 48+8 t=1 type.[1]interface {}+0
type.[1]interface {} SRODATA dupok size=72
	0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
	0x0010 50 91 5b fa 02 08 08 11 00 00 00 00 00 00 00 00  P.[.............
	0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0040 01 00 00 00 00 00 00 00                          ........
	rel 24+8 t=1 runtime.nilinterequal路f+0
	rel 32+8 t=1 runtime.gcbits.02+0
	rel 40+4 t=5 type..namedata.*[1]interface {}-+0
	rel 44+4 t=6 type.*[1]interface {}+0
	rel 48+8 t=1 type.interface {}+0
	rel 56+8 t=1 type.[]interface {}+0
""..inittask SNOPTRDATA size=32
	0x0000 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00  ................
	0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	rel 24+8 t=1 fmt..inittask+0
go.itab.*os.File,io.Writer SRODATA dupok size=32
	0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	0x0010 44 b5 f3 33 00 00 00 00 00 00 00 00 00 00 00 00  D..3............
	rel 0+8 t=1 type.io.Writer+0
	rel 8+8 t=1 type.*os.File+0
	rel 24+8 t=1 os.(*File).Write+0
go.itablink.*os.File,io.Writer SRODATA dupok size=8
	0x0000 00 00 00 00 00 00 00 00                          ........
	rel 0+8 t=1 go.itab.*os.File,io.Writer+0
type..importpath.fmt. SRODATA dupok size=6
	0x0000 00 00 03 66 6d 74                                ...fmt
gclocals路e6397a44f8e1b6e77d0f200b4fba5269 SRODATA dupok size=10
	0x0000 02 00 00 00 03 00 00 00 01 00                    ..........
gclocals路69c1753bd5f81501d95132d08af04464 SRODATA dupok size=8
	0x0000 02 00 00 00 00 00 00 00                          ........
gclocals路9fb7f0986f647f17cb53dda1484e0f7a SRODATA dupok size=10
	0x0000 02 00 00 00 01 00 00 00 00 01                    ..........
gclocals路1a65e721a2ccc325b382662e7ffee780 SRODATA dupok size=10
	0x0000 02 00 00 00 01 00 00 00 01 00                    ..........
gclocals路568470801006e5c0dc3947ea998fe279 SRODATA dupok size=10
	0x0000 02 00 00 00 02 00 00 00 00 02                    ..........
gclocals路bfec7e55b3f043d1941c093912808913 SRODATA dupok size=11
	0x0000 03 00 00 00 02 00 00 00 00 01 03                 ...........
"".main.stkobj SRODATA size=24
	0x0000 01 00 00 00 00 00 00 00 f0 ff ff ff ff ff ff ff  ................
	0x0010 00 00 00 00 00 00 00 00                          ........
	rel 16+8 t=1 type.[1]interface {}+0

fmt.Println()j进行分析

2.分析func Println(a …interface{}) (n int, err error)

src\fmt\print.go

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}

fmt 包的 Println() 方法只是调用了 fmt 包下的 Fprintln() 方法。

func Fprintln(w io.Writer, a …interface{}) (n int, err error)

src\fmt\print.go

// These routines end in 'ln', do not take a format string,
// always add spaces between operands, and add a newline
// after the last operand.

// Fprintln formats using the default formats for its operands and writes to w.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
	p := newPrinter()
	p.doPrintln(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}

参数:

  • 1.io.Writer;
  • 2.打印的数据.

1.调用newPrinter( )申请临时对象池
2.从对象池中的对象调用doPrintln()方法
3.io.Writer调用write方法
4.释放对象

1.func newPrinter() *pp

src\fmt\print.go

// newPrinter allocates a new pp struct or grabs a cached one.
func newPrinter() *pp {
	p := ppFree.Get().(*pp)
	p.panicking = false
	p.erroring = false
	p.wrapErrs = false
	p.fmt.init(&p.buf)
	return p
} 

1.newPrinter( )通过ppFree.Get( )申请了一个临时对象池;
2.p.panicking :由catchPanic设置,是为了避免在panic和recover中无限循环
3.p.erroring:当打印错误的标识符的时候,防止调用handleMethods
4.p.wrapErrs:当格式字符串包含了动词时的设置
5.p.fmt.init( ):初始化 fmt 配置,会设置 buf 并且清空 fmtFlags 标志位

1.ppFree

src\fmt\print.go

var ppFree = sync.Pool{
	New: func() interface{} { return new(pp) },
}

go的临时对象池sync.Pool,池中存储着被分配了但未使用的对象,但是未来可能会使用的值。从而实现减少 GC的压力。

2.ppFree.Get()

src\sync\pool.go

// Get selects an arbitrary item from the Pool, removes it from the
// Pool, and returns it to the caller.
// Get may choose to ignore the pool and treat it as empty.
// Callers should not assume any relation between values passed to Put and
// the values returned by Get.
//
// If Get would otherwise return nil and p.New is non-nil, Get returns
// the result of calling p.New.
func (p *Pool) Get() interface{} {
	if race.Enabled {
		race.Disable()
	}
	l, pid := p.pin()
	x := l.private
	l.private = nil
	if x == nil {
		// Try to pop the head of the local shard. We prefer
		// the head over the tail for temporal locality of
		// reuse.
		x, _ = l.shared.popHead()
		if x == nil {
			x = p.getSlow(pid)
		}
	}
	runtime_procUnpin()
	if race.Enabled {
		race.Enable()
		if x != nil {
			race.Acquire(poolRaceAddr(x))
		}
	}
	if x == nil && p.New != nil {
		x = p.New()
	}
	return x
}

Get会从临时对象池中任意选一个printer返回给调用者,并且将此项从对象池中移除。
Get也可以选择把临时对象池当成空的忽略。调用者不应该假设传递给Put方法的值和Get返回的值之间存在任何关系。
如果Get函数没有获取到资源但是p.New函数可以申请到新的资源,就直接返回p.New的值

3.pp

src\fmt\print.go

// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
	buf buffer

	// arg holds the current item, as an interface{}.
	arg interface{}

	// value is used instead of arg for reflect values.
	value reflect.Value

	// fmt is used to format basic items such as integers or strings.
	fmt fmt

	// reordered records whether the format string used argument reordering.
	reordered bool
	// goodArgNum records whether the most recent reordering directive was valid.
	goodArgNum bool
	// panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.
	panicking bool
	// erroring is set when printing an error string to guard against calling handleMethods.
	erroring bool
	// wrapErrs is set when the format string may contain a %w verb.
	wrapErrs bool
	// wrappedErr records the target of the %w verb.
	wrappedErr error
}
4.func (f *fmt) init(buf *buffer)

src\fmt\format.go

package fmt

func (f *fmt) init(buf *buffer) {
	f.buf = buf
	f.clearflags()
}

2.func (p *pp) doPrintln(a []interface{})

src\fmt\print.go

// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
func (p *pp) doPrintln(a []interface{}) {
	for argNum, arg := range a {
		if argNum > 0 {
			p.buf.writeByte(' ')
		}
		p.printArg(arg, 'v')
	}
	p.buf.writeByte('\n')
}

func (p *pp) printArg(arg interface{}, verb rune)
func (p *pp) printArg(arg interface{}, verb rune) {
	p.arg = arg
	p.value = reflect.Value{}

	if arg == nil {
		switch verb {
		case 'T', 'v':
			p.fmt.padString(nilAngleString)
		default:
			p.badVerb(verb)
		}
		return
	}

	// Special processing considerations.
	// %T (the value's type) and %p (its address) are special; we always do them first.
	switch verb {
	case 'T':
		p.fmt.fmtS(reflect.TypeOf(arg).String())
		return
	case 'p':
		p.fmtPointer(reflect.ValueOf(arg), 'p')
		return
	}

	// Some types can be done without reflection.
	switch f := arg.(type) {
	case bool:
		p.fmtBool(f, verb)
	case float32:
		p.fmtFloat(float64(f), 32, verb)
	case float64:
		p.fmtFloat(f, 64, verb)
	case complex64:
		p.fmtComplex(complex128(f), 64, verb)
	case complex128:
		p.fmtComplex(f, 128, verb)
	case int:
		p.fmtInteger(uint64(f), signed, verb)
	case int8:
		p.fmtInteger(uint64(f), signed, verb)
	case int16:
		p.fmtInteger(uint64(f), signed, verb)
	case int32:
		p.fmtInteger(uint64(f), signed, verb)
	case int64:
		p.fmtInteger(uint64(f), signed, verb)
	case uint:
		p.fmtInteger(uint64(f), unsigned, verb)
	case uint8:
		p.fmtInteger(uint64(f), unsigned, verb)
	case uint16:
		p.fmtInteger(uint64(f), unsigned, verb)
	case uint32:
		p.fmtInteger(uint64(f), unsigned, verb)
	case uint64:
		p.fmtInteger(f, unsigned, verb)
	case uintptr:
		p.fmtInteger(uint64(f), unsigned, verb)
	case string:
		p.fmtString(f, verb)
	case []byte:
		p.fmtBytes(f, verb, "[]byte")
	case reflect.Value:
		// Handle extractable values with special methods
		// since printValue does not handle them at depth 0.
		if f.IsValid() && f.CanInterface() {
			p.arg = f.Interface()
			if p.handleMethods(verb) {
				return
			}
		}
		p.printValue(f, verb, 0)
	default:
		// If the type is not simple, it might have methods.
		if !p.handleMethods(verb) {
			// Need to use reflection, since the type had no
			// interface methods that could be used for formatting.
			p.printValue(reflect.ValueOf(f), verb, 0)
		}
	}
}
func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune)

src\fmt\print.go

 // fmtInteger formats a signed or unsigned integer.
func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) {
	switch verb {
	case 'v':
		if p.fmt.sharpV && !isSigned {
			p.fmt0x64(v, true)
		} else {
			p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
		}
	case 'd':
		p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
	case 'b':
		p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits)
	case 'o', 'O':
		p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits)
	case 'x':
		p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits)
	case 'X':
		p.fmt.fmtInteger(v, 16, isSigned, verb, udigits)
	case 'c':
		p.fmt.fmtC(v)
	case 'q':
		if v <= utf8.MaxRune {
			p.fmt.fmtQc(v)
		} else {
			p.badVerb(verb)
		}
	case 'U':
		p.fmt.fmtUnicode(v)
	default:
		p.badVerb(verb)
	}
}
func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string)
// fmtInteger formats signed and unsigned integers.
func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) {
	negative := isSigned && int64(u) < 0
	if negative {
		u = -u
	}

	buf := f.intbuf[0:]
	// The already allocated f.intbuf with a capacity of 68 bytes
	// is large enough for integer formatting when no precision or width is set.
	if f.widPresent || f.precPresent {
		// Account 3 extra bytes for possible addition of a sign and "0x".
		width := 3 + f.wid + f.prec // wid and prec are always positive.
		if width > len(buf) {
			// We're going to need a bigger boat.
			buf = make([]byte, width)
		}
	}

	// Two ways to ask for extra leading zero digits: %.3d or %03d.
	// If both are specified the f.zero flag is ignored and
	// padding with spaces is used instead.
	prec := 0
	if f.precPresent {
		prec = f.prec
		// Precision of 0 and value of 0 means "print nothing" but padding.
		if prec == 0 && u == 0 {
			oldZero := f.zero
			f.zero = false
			f.writePadding(f.wid)
			f.zero = oldZero
			return
		}
	} else if f.zero && f.widPresent {
		prec = f.wid
		if negative || f.plus || f.space {
			prec-- // leave room for sign
		}
	}

	// Because printing is easier right-to-left: format u into buf, ending at buf[i].
	// We could make things marginally faster by splitting the 32-bit case out
	// into a separate block but it's not worth the duplication, so u has 64 bits.
	i := len(buf)
	// Use constants for the division and modulo for more efficient code.
	// Switch cases ordered by popularity.
	switch base {
	case 10:
		for u >= 10 {
			i--
			next := u / 10
			buf[i] = byte('0' + u - next*10)
			u = next
		}
	case 16:
		for u >= 16 {
			i--
			buf[i] = digits[u&0xF]
			u >>= 4
		}
	case 8:
		for u >= 8 {
			i--
			buf[i] = byte('0' + u&7)
			u >>= 3
		}
	case 2:
		for u >= 2 {
			i--
			buf[i] = byte('0' + u&1)
			u >>= 1
		}
	default:
		panic("fmt: unknown base; can't happen")
	}
	i--
	buf[i] = digits[u]
	for i > 0 && prec > len(buf)-i {
		i--
		buf[i] = '0'
	}

	// Various prefixes: 0x, -, etc.
	if f.sharp {
		switch base {
		case 2:
			// Add a leading 0b.
			i--
			buf[i] = 'b'
			i--
			buf[i] = '0'
		case 8:
			if buf[i] != '0' {
				i--
				buf[i] = '0'
			}
		case 16:
			// Add a leading 0x or 0X.
			i--
			buf[i] = digits[16]
			i--
			buf[i] = '0'
		}
	}
	if verb == 'O' {
		i--
		buf[i] = 'o'
		i--
		buf[i] = '0'
	}

	if negative {
		i--
		buf[i] = '-'
	} else if f.plus {
		i--
		buf[i] = '+'
	} else if f.space {
		i--
		buf[i] = ' '
	}

	// Left padding with zeros has already been handled like precision earlier
	// or the f.zero flag is ignored due to an explicitly set precision.
	oldZero := f.zero
	f.zero = false
	f.pad(buf[i:])
	f.zero = oldZero
}

buf := f.intbuf[0:]
// The already allocated f.intbuf with a capacity of 68 bytes
// is large enough for integer formatting when no precision or width is set.

type fmt struct
// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
	buf *buffer

	fmtFlags

	wid  int // width
	prec int // precision

	// intbuf is large enough to store %b of an int64 with a sign and
	// avoids padding at the end of the struct on 32 bit architectures.
	intbuf [68]byte
}

// intbuf is large enough to store %b of an int64 with a sign and
// avoids padding at the end of the struct on 32 bit architectures.
intbuf [68]byte

3.Write(p []byte)

src\io\io.go

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
type Writer interface {
	Write(p []byte) (n int, err error)
}

创建了buffer,使用io进行输出了,划分的是堆的空间进行,所以逃逸到堆。
所以一般使用中,尽可能少用fmt.Print xxx这种,建议使用内置函数 func println(args …Type)来打印

4.func (p *pp) free()

// free saves used pp structs in ppFree; avoids an allocation per invocation.
func (p *pp) free() {
	// Proper usage of a sync.Pool requires each entry to have approximately
	// the same memory cost. To obtain this property when the stored type
	// contains a variably-sized buffer, we add a hard limit on the maximum buffer
	// to place back in the pool.
	//
	// See https://golang.org/issue/23199
	if cap(p.buf) > 64<<10 {
		return
	}

	p.buf = p.buf[:0]
	p.arg = nil
	p.value = reflect.Value{}
	p.wrappedErr = nil
	ppFree.Put(p)
}

通过Put函数将其放回临时对象池中,已备下次调用。

3.泄露参数

如果出现:go build -gcflags=-m xxx.go时出现 leaking param 则发生泄露参数
泄露参数不一定造成逃逸

  1. 作用域没有改变 : 不一定会造成逃逸
  2. 作用域发生改变:必然造成逃逸【参考指针逃逸】
    作用域未改变
package main

type Student struct {
	id   string
	Name string
	Age  int
}

func NewStudent(stu *Student) *Student {
	return stu
}
func main() {
	stu := &Student{
		Name: "Lisa",
		Age:  18,
	}
	NewStudent(stu)
}

 go build -gcflags '-m -l' .\eclipse.go

在这里插入图片描述

NewStudent( )只是直接返回了stu,并没有发生逃逸,虽然stu是泄露参数,stu的作用域还是在main( ),可以参照作用域【指针逃逸】的判定来判断

4.栈空间不足逃逸

package main

func main() {
	s := make([]int, 1<<13-1, 1<<13-1) // make([]int, 1 << 13 - 1, 1 << 13 - 1) does not escape [cap ==>>>>1<<13-1===2^13-1===8191]
	println(len(s))
	println(cap(s))
}

在这里插入图片描述

package main

func main() {
	s := make([]int, 1<<13, 1<<13) // make([]int, 1 << 13, 1 << 13) escapes to heap [cap ==>>>>1<<13===2^13===8192]
	println(len(s))
	println(cap(s))
}

在这里插入图片描述

测试环境:
OS:Windows x64
内存:32G
注意:
slice的cap【容量】的大小在测试环境中可知小于等于8191【2^13-1,1<<13-1】不会发生逃逸,否则会因为栈空间不足导致逃逸。

当栈空间不足以存放当前对象时或无法判断当前切片长度时会将对象分配到堆中。

5.闭包引用对象逃逸

package main

func Fibonacci() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}
func main() {
	f := Fibonacci()
	for i := 0; i < 10; i++ {
		println(f())
	}
}

在这里插入图片描述

1.Fibonacci( )函数返回一个闭包,闭包引用了函数的局部变量a和b,使用时通过该函数获取该闭包,然后每次执行闭包都会依次输出Fibonacci数列。
2.Fibonacci()函数中原本属于局部变量的a和b由于闭包的引用,不得不将二者放到堆上,以致产生逃逸。

5.逃逸分析总结

1.栈上分配内存比在堆中分配内存有更高的效率
2.栈上分配的内存不需要GC处理
3.堆上分配的内存使用完毕会交给GC处理
4.逃逸分析目的是决定内分配地址是栈还是堆
5.逃逸分析在编译阶段完成

6.注意

传递指针可以减少底层值的拷贝,可以提高效率,但是如果拷贝的数据量小,由于指针传递会产生逃逸,可能会使用堆,也可能会增加GC的负担,所以传递指针不一定是高效的

发布了48 篇原创文章 · 获赞 14 · 访问量 4153

猜你喜欢

转载自blog.csdn.net/weixin_42366378/article/details/105132890