逃逸分析

最后更新于:2022-04-02 02:36:22

[TOC] ## 逃逸分析 ### 指针逃逸 - 即在函数中创建了一个对象,返回了这个对象的指针。这种情况下,函数虽然退出了,但是因为指针的存在,对象的内存不能随着函数结束而回收,因此只能分配在堆上。 - 函数`createDemo`的局部变量`d`发生了逃逸。d 作为返回值,在 main 函数中继续使用,因此 d 指向的内存不能够分配在栈上,随着函数结束而回收,只能分配在堆上 ``` // main_pointer.go package main import "fmt" type Demo struct { name string } func createDemo(name string) *Demo { d := new(Demo) // 局部变量 d 逃逸到堆 d.name = name return d } func main() { demo := createDemo("demo") fmt.Println(demo) } ``` 结果 ``` $ go build -gcflags=-m main_pointer.go ./main_pointer.go:10:6: can inline createDemo ./main_pointer.go:17:20: inlining call to createDemo ./main_pointer.go:18:13: inlining call to fmt.Println ./main_pointer.go:10:17: leaking param: name ./main_pointer.go:11:10: new(Demo) escapes to heap ./main_pointer.go:17:20: new(Demo) escapes to heap ./main_pointer.go:18:13: demo escapes to heap ./main_pointer.go:18:13: main []interface {} literal does not escape ./main_pointer.go:18:13: io.Writer(os.Stdout) escapes to heap :1: (*File).close .this does not escape ``` ### 栈空间不足 操作系统对内核线程使用的栈空间是有大小限制的,64 位系统上通常是 8 MB。可以使用`ulimit -a`命令查看机器上栈允许占用的内存的大小。 ``` $ ulimit -a -s: stack size (kbytes) 8192 -n: file descriptors 12800 ... ``` ``` func generate8191() { nums := make([]int, 8191) // < 64KB for i := 0; i < 8191; i++ { nums[i] = rand.Int() } } func generate8192() { nums := make([]int, 8192) // = 64KB for i := 0; i < 8192; i++ { nums[i] = rand.Int() } } func generate(n int) { nums := make([]int, n) // 不确定大小 for i := 0; i < n; i++ { nums[i] = rand.Int() } } func main() { generate8191() generate8192() generate(1) } ``` 运行 ``` $ go build -gcflags=-m main_stack.go # command-line-arguments ./main_stack.go:9:14: generate8191 make([]int, 8191) does not escape ./main_stack.go:16:14: make([]int, 8192) escapes to heap ./main_stack.go:23:14: make([]int, n) escapes to heap ``` `make([]int, 8191)`没有发生逃逸,`make([]int, 8192)`和`make([]int, n)`逃逸到堆上,也就是说,当切片占用内存超过一定大小,或无法确定当前切片长度时,对象占用内存将在堆上分配 ### 闭包 ``` func Increase() func() int { n := 0 return func() int { n++ return n } } func main() { in := Increase() fmt.Println(in()) // 1 fmt.Println(in()) // 2 } ```
';