在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
堆和栈要理解什么是逃逸分析会涉及堆和栈的一些基本知识,如果忘记的同学我们可以简单的回顾一下:
栈分配内存只需要两个CPU指令:“PUSH”和“RELEASE”,分配和释放;而堆分配内存首先需要去找到一块大小合适的内存块,之后要通过垃圾回收才能释放。 通俗比喻的说,
什么是逃逸分析在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法,简单来说就是分析在程序的哪些地方可以访问到该指针。 再往简单的说,Go是通过在编译器里做逃逸分析(escape analysis)来决定一个对象放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上;即我发现
为何需要逃逸分析ok,了解完
如何确定是否逃逸在 go run -gcflags '-m -l' main.go
逃逸案例案例一:取地址发生逃逸package main type UserData struct { Name string } func main() { var info UserData info.Name = "WilburXu" _ = GetUserInfo(info) } func GetUserInfo(userInfo UserData) *UserData { return &userInfo }
执行 # command-line-arguments .\main.go:14:9: &userInfo escapes to heap .\main.go:13:18: moved to heap: userInfo
优化方案func main() { var info UserData info.Name = "WilburXu" _ = GetUserInfo(&info) } func GetUserInfo(userInfo *UserData) *UserData { return userInfo }
# command-line-arguments .\main.go:13:18: leaking param: userInfo to result ~r1 level=0 .\main.go:10:18: main &info does not escape 对一个变量取地址,可能会被分配到堆上。但是编译器进行逃逸分析后,如果发现到在函数返回后,此变量不会被引用,那么还是会被分配到栈上。套个取址符,就想骗补助? 编译器傲娇的说:Too young,Too Cool...!
案例二 :未确定类型package main type User struct { name interface{} } func main() { name := "WilburXu" MyPrintln(name) } func MyPrintln(one interface{}) (n int, err error) { var userInfo = new(User) userInfo.name = one // 泛型赋值 逃逸咯 return }
执行 # command-line-arguments ./main.go:12:16: leaking param: one ./main.go:13:20: MyPrintln new(User) does not escape ./main.go:9:11: name escapes to heap
这里可能有同学会好奇, 上一个案例我们知道了,普通的手法想去"骗取补助",聪明灵利的编译器是不会“上当受骗的噢”;但是对于 优化方案将结构体 # command-line-arguments ./main.go:12:16: leaking param: one ./main.go:13:20: MyPrintln new(User) does not escape 拓展分析对于案例二的分析,我们还可以通过反编译命令 # main.go:9 -> MyPrintln(name) 0x001d 00029 (main.go:9) PCDATA $2, $1 0x001d 00029 (main.go:9) PCDATA $0, $1 0x001d 00029 (main.go:9) LEAQ go.string."WilburXu"(SB), AX 0x0024 00036 (main.go:9) PCDATA $2, $0 0x0024 00036 (main.go:9) MOVQ AX, ""..autotmp_5+32(SP) 0x0029 00041 (main.go:9) MOVQ $8, ""..autotmp_5+40(SP) 0x0032 00050 (main.go:9) PCDATA $2, $1 0x0032 00050 (main.go:9) LEAQ type.string(SB), AX 0x0039 00057 (main.go:9) PCDATA $2, $0 0x0039 00057 (main.go:9) MOVQ AX, (SP) 0x003d 00061 (main.go:9) PCDATA $2, $1 0x003d 00061 (main.go:9) LEAQ ""..autotmp_5+32(SP), AX 0x0042 00066 (main.go:9) PCDATA $2, $0 0x0042 00066 (main.go:9) MOVQ AX, 8(SP) 0x0047 00071 (main.go:9) CALL runtime.convT2Estring(SB) 对于
总结不要盲目使用变量的指针作为函数参数,虽然它会减少复制操作。但其实当参数为变量自身的时候,复制是在栈上完成的操作,开销远比变量逃逸后动态地在堆上分配内存少的多。 Go的编译器就如一个聪明的
参考文章
|
请发表评论