逃逸规定

  1. 如果函数内部没有援用,则优先(不是肯定)放到栈中;
  2. 如果调配的内存超过了栈的存储能力,会在堆上调配
  3. 如果函数内部存在援用,则必然放到堆中;
  4. 一个援用类对象中的any(interface{})进行赋值,会呈现逃逸景象。
  5. 一个援用类对象中的援用类成员进行赋值,会呈现逃逸景象。

个别咱们给一个援用类对象中的援用类成员进行赋值,可能呈现逃逸景象。能够了解为拜访一个援用对象实际上底层就是通过一个指针来间接的拜访了,但如果再拜访外面的援用成员就会有第二次间接拜访,这样操作这部分对象的话,极大可能会呈现逃逸的景象。Go语言中的援用类型有func(函数类型),interface(接口类型),slice(切片类型),map(字典类型),channel(管道类型),*(指针类型)等。

对于后面3条咱们很好了解,咱们当初来看第4点。

示例一 map的value是any

package mainfunc main() {    data := make(map[int]interface{})    data[100] = 200}//go build -gcflags=-m .\main.go//main.go:3:6: can inline main//main.go:4:14: make(map[int]interface {}) does not escape//main.go:5:14: 200 escapes to heap

后果是200逃逸到堆内存中去了。

示例二 map里的key和value类型都确定

package mainfunc main() {    data := make(map[int]int)    data[100] = 200}/*go build -gcflags=-m .\main.go# command-line-arguments./main.go:3:6: can inline main./main.go:4:14: make(map[int]int) does not escape*/

后果是没有产生逃逸

示例三 map的key和value都未确定类型

package mainfunc main() {    data := make(map[any]any)    data[100] = 200}/*go build -gcflags=-m .\main.go# command-line-arguments./main.go:3:6: can inline main./main.go:4:14: make(map[any]any) does not escape./main.go:5:7: 100 escapes to heap./main.go:5:14: 200 escapes to heap*/

因为map自身是一个援用类型,而当其键和值转换为interface{}类型,这导致了100和200的逃逸到堆上。编译器无奈确定这两个值的具体类型,因而它将它们调配到堆上以确保安全性。在示例三种编译器能够确定key和value的类型都是int,就将其调配到了栈上。

对于第五点咱们再来看几个示例:

示例四 map外部应用切片

package mainfunc main() {    data := make(map[string][]string)    data["key"] = []string{"value"}}//main.go:3:6: can inline main//main.go:4:14: make(map[string][]string) does not escape//main.go:5:24: []string{...} escapes to heap

示例五 切片

package mainfunc main() {    data := []string{"value"}    data = append(data, "")}/*./main.go:3:6: can inline main./main.go:4:18: []string{...} does not escape*/

如果data自身是个map(援用类型),在map外面的value又是一个slice(切片类型)也是援用类型,那这个切片就会溢出。

示例六 切片外部应用指针

package mainfunc main() {    a := 10    data := []*int{nil}    data[0] = &a}/*./main.go:3:6: can inline main./main.go:4:2: moved to heap: a./main.go:5:16: []*int{...} does not escape*/

如果data自身是个切片(援用类型),在data外面的value又是一个int也是援用类型,那这个int类型的a就会溢出。

示例七 通道外部应用援用类型

package mainfunc main() {    ch := make(chan []string)    s := []string{"aceld"}    go func() {        ch <- s    }()}/*./main.go:8:5: can inline main.func1./main.go:6:15: []string{...} escapes to heap./main.go:8:5: func literal escapes to heap*/

示例八 函数参数应用援用类型

package mainimport "fmt"func foo(a *int) {    return}func main() {    data := 10    f := foo    f(&data)    fmt.Println(data)}/*./main.go:5:6: can inline foo./main.go:12:3: inlining call to foo./main.go:13:13: inlining call to fmt.Println./main.go:5:10: a does not escape./main.go:13:13: ... argument does not escape./main.go:13:14: data escapes to heap*/