逃逸分析
逃逸分析的好处:
1 逃逸分析的好处是为了减少gc的压力,不逃逸的对象分配在栈上,当函数返回时就回收了资源,不需要gc标记清除。
2 逃逸分析完后可以确定哪些变量可以分配在栈上,栈的分配比堆快,性能好(逃逸的局部变量会在堆上分配 ,而没有发生逃逸的则有编译器在栈上分配)。
3 同步消除,如果你定义的对象的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。
四种情况会逃逸:1 函数内new后,返回指针 2 空间不足,如用make函数申请的空间过大,会分配到堆上,3 类型不确定 a 赋值给接口类型,b make申请的空间大小不确定, 4 闭包内的变量,
1 编译原理中,分析指针动态范围的方法称之为逃逸分析,Go语言的逃逸分析是编译器执行静态代码分析后,对内存管理进行的优化和简化,它可以决定一个变量是分配到堆还栈上。
go在编译的时候进行逃逸分析,来决定一个对象放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上。
编译器会根据变量是否被外部引用来决定是否逃逸:如果在函数外面没有引用到,则优先放到栈区中;如果在函数外面存在引用的可能,则就会放到堆区中;
//1 将变量取地址返回 //func test() *User{ // a := User{} // return &a //} //// 当输入的变量或指针,直接返回时,不会逃逸, //type S struct {} //func main() { // var x S // y := &x // _ = fun1(y) // _ = fun2(x) //} //// 输入直接当成返回值了,因为没有对z作引用,所以z没有逃逸 //func fun1(z *S) *S { // return z //} //func fun2(k S) S { // return k //} //// z是对x的拷贝,ref函数中对z取了引用,所以z不能放在栈上, //// 否则在ref函数之外,通过引用如何找到z,所以z必须要逃逸到堆上 //package main //type S struct {} //func main() { // var x S // _ = *ref(x) //} //func ref(z S) *S { // return &z //} // refStruct函数对y取了引用,所以y发生了逃逸,z没有逃逸, type S struct { M *int } func main() { var i int i = 2 refStruct(i) } // z不会逃逸,因为z是普通变量,返回z的时候是值拷贝, func refStruct(y int) (z S) { z.M = &y return z } // 这里的z会逃逸,因为返回的是指针,且z是内部开辟的内存, //func refStruct(y int) (z *S) { // z = new(S) // z.M = &y // return z //} // 这里的y没有逃逸,z也没有逃逸, //type S struct { // M *int //} //func main() { // var i int // refStruct(&i) //} //func refStruct(y *int) (z S) { // z.M = y // return z //} ////3 被指针类型的slice、map和chan引用的指针,注意必须是指针,一定发生逃逸 //func main() { // a := make([]*int,1) // b := 12 // a[0] = &b // // c := make(map[string]*int) // d := 14 // c["aaa"]=&d // // e := make(chan *int,1) // f := 15 // e <- &f //}