go-逃逸分析

147 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

1、常见的逃逸现象

编译器根据代码的特征和生命周期,自动的把变量分配到堆或者是栈上面。在编译阶段才能确认逃逸,人不是在运行时。

1、返回函数类型逃逸

1、返回一个函数类型的时候就会发生逃逸。

package main

import "fmt"

func main() {
    
   test := testfunc()

   fmt.Println(name())

}
//testfunc 返回一个 func()string{} 匿名函数
func testfunc() func() string {
    // func()返回一个string类型
   return func() string {

      return "函数逃逸"

   }
}

控制台输入命令:

//-m为内存分析  -l防止内联优化
go build -gcflags="-m -l" funcType.go //funcType为文件名

返回结果:

1653576515(1).png 很显然,name() 逃逸到了堆里

2、在方法内把局部变量指针返回

package main
import "fmt"
//定义一个结构体
type X struct {
 s string
}
//
func foo(s string) *X {
 x := new(X) 
 x.s = s
 return x //返回局部变量x,在C语言中属于是野指针,会报错,但在go则不报错,但a会逃逸到堆
}
func main() {
//将函数的返回的*X赋值给a
 a := foo("逃")
 //字符串拼接
 b := a.s + "逸"
 c := b + "了!"
 fmt.Println(c)
}
//运行结果:
逃逸了!

控制台输入命令 输出结果:

image.png

结果分析: new(x)逃逸到了堆

b+"了" 逃逸···

3、除了这两个行为会发生逃逸现象以外,还有很多。

比如: 1、发送指针或带有指针的值到 channel 中。

2、在一个切片上存储指针或带指针的值。

3、slice 的背后数组被重新分配了(超出容量)

4、channel 或者栈空间不足逃逸

4、堆和栈

简单来说:栈( stack)是系统自动分配空间的,堆是由程序员自己申请的

栈在内存中是从高地址向下分配的,并且连续的,遵循先进后出原则。(存数据相当于砌墙,检索数据相当于拆墙。)

堆分配是从低地址向高地址分配的,每次分配的内存大小可能不一致,导致了空间是不连续的,这也产生内存碎片的原因。所以速度就会更慢。