1、什么是逃逸分析?

逃逸分析是一种确定指针动态范围的方法,可以分析在程序的哪些地方可以访问到指针。

2、逃逸分析做了什么?

Go是通过在编译期间通过编译器来进行逃逸分析,决定一个变量是放堆上还是栈上。

3、所以怎么知道变量是分配在栈(stack)上还是堆(heap)上?

Go官网上有一段可以表明分配的规则:

准确来说,你并不需要知道。Golang 中的变量只要被引用就一直会存活,存储在堆上还是栈上由内部实现决定而和具体的语法没有关系。知道变量的存储位置确实对程序的效率有帮助。如果可能,Golang 编译器会将函数的局部变量分配到函数栈帧(stack frame)上。然而,如果编译器不能确保变量在函数 return 之后不再被引用,编译器就会将变量分配到堆上。而且,如果一个局部变量非常大,那么它也应该被分配到堆上而不是栈上。当前情况下,如果一个变量被取地址,那么它就有可能被分配到堆上。然而,还要对这些变量做逃逸分析,如果函数 return 之后,变量不再被引用,则将其分配到栈上。

总结一下:

  • 如果局部变量在函数外被引用了,变量会被分配到堆上。
  • 如果一个局部变量非常大,无法放在栈上,也会被放到堆上。
  • 如果一个局部变量在函数return之后没有被引用了,就会将其放到栈上。

4、逃逸分析的作用(好处)是什么?

通过逃逸分析决定了不逃逸的对象会被分配在栈上,函数返回时就回收了资源,不需要gc标记清除,减少了GC的压力。

相反的,如果没有逃逸分析把所有对象都分配到堆上会导致如下问题:

  • GC的压力不断增大
  • 申请、分配、回收内存的系统开销增大
  • 动态分配产生一定量的内存碎片

5、怎么知道变量是否发生了逃逸?

go build -gcflags '-m -l'
package main

func getStr() *string{
	str := new(string)
	*str = "chasel1"
	str2 := "chasel2"
	return &str2
}

func main() {
	_ = getStr()
}

执行命令并输出:

$ go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:6:2: moved to heap: str2
./main.go:4:12: new(string) does not escape

通过查看分析我们可以知道str2被函数外部引用了,发生了逃逸,分配到堆上,而另外一个对象则是没有发生逃逸的。