在Go语言中,逃逸分析是编译时的一个过程,用来分析变量分配在栈上还是堆上。如果一个变量在函数外部仍然可以访问,那么这个变量就会从栈上“逃逸”到堆上。以下是一些常见的逃逸场景:

  1. 返回局部变量的指针:如果你的函数返回了一个指向局部变量的指针,那么这个局部变量就会逃逸到堆上。
1
2
3
4
func newObject() *Object {
    o := Object{}
    return &o // o escapes to heap
}
  1. 将局部变量的指针传递给函数:如果你将局部变量的指针传递给了其他函数,而这个函数可能在当前函数返回后还需要访问这个变量,那么它就会逃逸。
1
2
3
4
func (s *SomeStruct) Modify() {
    localVar := 42
    s.modifyHelper(&localVar) // localVar escapes to heap
}
  1. 闭包引用局部变量:如果你创建了一个闭包,并且这个闭包引用了它外部函数的局部变量,如果闭包的生命周期可能超过该函数的生命周期,那么这个局部变量就会逃逸。
1
2
3
4
5
6
func startRoutine() {
    localVar := 42
    go func() {
        fmt.Println(localVar)
    }() // localVar escapes to heap because it's captured by the closure
}
  1. 动态类型和接口:如果一个局部变量被存储在一个接口类型里面,这个接口可能被未知的代码所持有,那么这个变量可能会逃逸。
1
2
3
func logValue(v interface{}) {
    fmt.Println(v) // v may escape to heap
}
  1. 变量太大:即使变量没有通过指针或闭包的方式逃逸,但如果它过于庞大,可能会直接分配在堆上,以避免栈溢出。
1
2
3
4
func bigLocalVariable() {
    var big [1024 * 1024]byte // may escape to heap to avoid stack overflow
    _ = big
}
  1. 切片操作:如果你创建了一个切片,并且这个切片的底层数组逃逸了,那么这个切片也会逃逸。
1
2
3
4
func sliceEscape() []byte {
    data := make([]byte, 1024)
    return data[:100] // data escapes to heap
}

要确定一个具体的变量是否会逃逸,你可以在编译Go代码时使用-gcflags ‘-m’标志,这会让编译器输出逃逸分析的信息。例如: go build -gcflags '-m' your_package 或者你可以针对单个文件进行分析: go build -gcflags '-m' your_file.go