内存逃逸
内存逃逸的含义
- 是指从栈上分配变为从堆上分配,go在编译的时候会进行逃逸分析,来决定一个对象是放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上。
逃逸场景
-
指针逃逸
go返回局部变量指针,在方法内将局部变量指针返回,例如:
package main type testStr struct { test string } func newTestStr(v string) *testStr { t := new(testStr) t.test = v return t } func main() { newTestStr("test") }
运行命令 go build -gcflags=-m
然后看执行结果:
.\testescapeanalysis.go:7:6: can inline newTestStr .\testescapeanalysis.go:14:6: can inline main .\testescapeanalysis.go:15:12: inlining call to newTestStr .\testescapeanalysis.go:7:17: leaking param: v .\testescapeanalysis.go:8:10: new(testStr) escapes to heap .\testescapeanalysis.go:15:12: new(testStr) does not escape
可以看到第五行出现escapes to heap
-
栈空间不足逃逸
空间开辟过大,在编译期间,如果知道自己的size,那么放入栈中,但是append的时候,可能不知道具体扩容数量,这个时候放入堆中,比如:
package main func testSlice() { s := make([]int, 10000, 10000) for i, _ := range s { s[i] = i } } func main() { testSlice() }
然后看执行结果:
# command-line-arguments .\testescapeanalysis.go:11:6: can inline main .\testescapeanalysis.go:4:11: make([]int, 10000, 10000) escapes to heap
当空间分配过大,出现内存逃逸
-
interface类型多态的时候
编译期间很难确定参数具体类型,也会产生逃逸,比如:
package main import "fmt" type Animal interface { voice() } type Cat struct{} func (cat Cat) voice() { fmt.Println("cat voice") } type Dog struct{} func (dog Dog) voice() { fmt.Println("cat voice") } func main() { var animal Animal animal = Cat{} animal.voice() // 调用方法时,发生逃逸,因为方法是动态分配的 animal = Dog{} animal.voice() }
然后看执行结果:
# command-line-arguments .\testescapeanalysis.go:11:6: can inline Cat.voice .\testescapeanalysis.go:12:13: inlining call to fmt.Println .\testescapeanalysis.go:17:6: can inline Dog.voice .\testescapeanalysis.go:18:13: inlining call to fmt.Println .\testescapeanalysis.go:12:14: "cat voice" escapes to heap .\testescapeanalysis.go:12:13: []interface {} literal does not escape .\testescapeanalysis.go:18:14: "cat voice" escapes to heap .\testescapeanalysis.go:18:13: []interface {} literal does not escape .\testescapeanalysis.go:23:9: Cat literal escapes to heap .\testescapeanalysis.go:25:9: Dog literal escapes to heap
-
闭包引用包外的值
由于被闭包引用,所以会放到堆上
-
chan中发送数据的指针或者包含指针的值
编译起并不知道何时释放,所以也是放到堆中
-
slice、map中存储指针或者包含指针的值
假使slice是在栈上,但是存储的值也会放到堆中