逃逸分析在编译阶段完成,目的是决定内存分配地址是栈还是堆:编译时通过 go build -gcflags=-m 可以查看逃逸对象
1、关于堆和栈
栈可以简单理解成一次函数调用内部申请到的内存,它们会随着函数的返回把内存还给系统。
在栈上申请的内存 :函数返回直接释放,不会引起垃圾回收,对性能没有影响。
在堆上申请的对象生命周期可以超出函数调用的作用域,需要gc进行回收。
2、看几个demo
1、申请临时变量不会逃逸
func a() {
t := make([]int, 10) // 这里是不会逃逸到堆上的
if len(t) == 1 {
}
}
2、函数结尾时把slice给返回了,会逃逸到堆上,因为编译器认为该对象后续会被使用
func a() []int {
t := make([]int, 10) // 返回切片,则会逃逸
return t
}
3、同2,返回指针和返回引用类型一样,会造成逃逸
type a struct{}
func NewA() *a {
return &a{} // 返回指针会逃逸
}
4、因为申请空间过大的对象,也会逃逸
func a() {
t := make([]int, 10)
t2 := make([]int, 10000)
if len(t) == 0 || len(t2) == 0 {
}
}
这两个临时变量,都没有作为返回值,但我们看到t2逃逸了。这里我们知道:申请空间过大的对象,也会逃逸
5、变长空间申请,会造成逃逸
func a() {
t := 10
t2 := make([]int, t) // 这里还是会逃逸
if len(t2) == 0 {
}
}
./main.go:11:12: make([]int, t) escapes to heap
编译时发现t2又逃逸了,原因是编译器不知道需要分配多大的空间给t2,编译器其实看到的cap是变量t,而不是10
3、以上总结
- 栈内存不需要gc,函数结束后回收
-
堆上分配的内存使用完毕会交给GC处理
-
逃逸分析在编译阶段完成,目的是决定对象分配到栈还是堆