逃逸分析是指编译器决定内存分配的位置。
在函数中申请一个对象:
分配在栈中:函数结束后可自动将内存回收。
分配在堆中:函数结束后交给GC处理。
逃逸策略
函数在申请对象时,根据该对象是否被函数外部引用来决定是否逃逸。
- 如果函数外部没有引用,优先放到栈中。
- 如果函数外部存在引用,一定放到堆中。
如果内存过大超过栈的存储能力时,则该对象放在堆中。
逃逸场景
指针逃逸
如果函数的返回值为指针的话则会发生逃逸。
例:
func StudentRegister(name string,age int) *Student {
s := new(Student)
s.Name = name
s.Age = age
return s
}
通过编译参数 -gcflag = -m 来查看编译过程中的逃逸分析过程。
栈空间不足逃逸
func Slice(){
s1 := make([]int,100,100)
s2 := make([]int,1000,1000)
s3 := make([]int,10000,10000)
}
从Slice中分配三个切片,是否逃逸取决于栈空间是否足够大。通过编译参数 -gcflag = -m 来查看是否逃逸。
事实上,当栈空间不足存放当前对象或者无法判断当前切片长度时都会将该对象分配给堆中。
动态类型逃逸
函数的参数为interface类型,比如fmt.Println(a …interface{}),编译过程很难确定其参数的具体类型,也会发生逃逸。
s := "Hello,World"
fmt.Println(s)
闭包引用对象逃逸
闭包中使用函数中的局部变量,则该局部变量放入堆中。
a , b := 1 , 2
func(){
a , b = 3 , 4
}