Go-逃逸分析

99 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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,也就是说发生了逃逸。