通过go build -gcflags=-m main.go查看逃逸情况

1.指针逃逸

函数返回值为局部变量的指针,函数虽然退出了,但因为指针的存在,指向的内存不能随着函数结束而回收,因此只能分配在堆上

package main
func escape1()* int{
    var a int = 1
    return &a
}
func main(){
    escape1()
}

2.栈空间不足

package main

func escape2() {
	s := make([]int, 0, 10000)
	for index, _ := range s {
		s[index] = index
	}
}
func main() {
	escape2()
}

3.变量大小不确定

package main

func escape3() {
	number := 10
	s := make([]int, number)
	for i := 0; i < len(s); i++ {
		s[i] = i
	}
}
func main() {
	escape3()
}

 编译期无法确定slice的长度,这种情况为了保证内存的安全,编译期也会触发逃逸,在堆上进行内存分配

直接s:=make([]int,10)不会发生逃逸

4.动态类型

动态类型就是编译期间不确定参数的类型、参数的长度也不确定的情况下就会发生逃逸

空接口interface{}可以表示任意的类型,如果函数参数为interface{},编译期间很难确定其参数的具体类型,也会发生逃逸

package main

import "fmt"

func escape4() {
	fmt.Println(1111)
}
func main() {
	escape4()
}

5.闭包引用对象

闭包函数中局部变量i在后续函数是继续使用的,编译器将其分配到堆上

package main

func escape5() func() int {
	var i int = 1
	return func() int {
		i++
		return i
	}
}
func main() {
	escape5()
}

总结:

1.栈上分配内存比堆中分配内存效率更高

2.栈上分配的内存不需要GC处理,而堆需要

3.逃逸分析目的是决定内存分配地址是栈还是堆

4.逃逸分析在编译阶段完成

无论变量大小,只要是指针变量都会在堆上分配,所以对于小变量使用传值(而不是传指针)效率更高