开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
逃逸分析
是什么
定义:逃逸分析就是决定一个变量是分配在堆上还是分配在栈上
- 区别:在栈上分配局部变量即静态内存分配,函数结束后变量占据的内存空间立刻会被销毁;在堆上分配空间,动态内存分配,程序退出时内存空间不会被销毁
作用是什么
C/C++中动态分配的内存需要手动释放,经常会出现忘记释放内存导致内存泄漏的问题。在Go语言中,栈和堆对程序员是透明的,逃逸分析通过将变量合理地分配到栈/堆上。Go只是申请了逻辑上的堆栈,本质都是操作系统层级的堆内存。
分配原则:
- 通过逃逸分析,分配的内存在函数退出之后没有继续被使用,分配到栈上
- 通过逃逸分析,分配的内存在函数退出之后继续被使用,分配到堆上
优缺点:
- 堆:适合不可预知大小的内存分配,分配速度慢,形成内存碎片,需要GC进行释放
- 栈:内存分配速度快,会被自动释放
如何完成
如果一个函数返回对一个变量的引用,那么这个变量就会发生逃逸
1)本该分配到栈上的变量,跑到了堆上,这就导致了内存逃逸。
2)栈是高地址到低地址,栈上的变量,函数结束后变量会跟着回收掉,不会有额外性能的开销。
3)变量从栈逃逸到堆上,如果要回收掉,需要进行 gc,那么 gc 一定会带来额外的性能开销。编程语言不断优化 gc 算法,主要目的都是为了减少 gc 带来的额外性能开销,变量一旦逃逸会导致性能开销变大。
内存逃逸的情况如下:
1)方法内返回局部变量指针。
2)向 channel 发送指针数据。
3)在闭包中引用包外的值。
4)在 slice 或 map 中存储指针。
5)切片(扩容后)长度太大。
6)在 interface 类型上调用方法。
如何确定
package main
import "fmt"
func foo() *int {
t := 3
return &t
}
func main() {
x := foo()
fmt.Println(*x)
}
go tool compile -S main.go
可以看到在编译阶段看到newobject这个函数,即在堆上分配了一块内存给t,也就是说发生了逃逸。