go语言机制和逃逸 go语言内存逃逸( 二 )


同理,如果函数的参数类型是slice,map或interface{}都会导致参数逃逸 。
匿名函数的调用也是一样的,它本质上也是一个函数变量 。有兴趣的可以自己测试一下 。
只要使用了Interface类型(不是interafce{}),那么赋值给它的变量一定会逃逸 。因为interfaceVariable.Method()先是间接的定位到它的实际值,再调用实际值的同名方法,执行时实际值作为参数传递给方法 。相当于interfaceVariable.Method.this = realValue
向 channel 中发送数据,本质上就是为 channel 内部的成员赋值,就像给一个 slice 中的某一项赋值一样 。所以chan *Type,chan map[Type]Type,chan []Type,chan interface{}类型都会导致发送到 channel 中的数据逃逸 。
这本来也是情理之中的,发送给 channel 的数据是要与其他函数分享的,为了保证发送过去的指针依然可用,只能使用堆分配 。
可变参数如func(arg ...string)实际与func(arg []string)是一样的,会增加一层访问路径 。这也是fmt.Sprintf总是会使参数逃逸的原因 。
例子非常多,这里不能一一列举,我们只需要记住分析方法就好,即,2 级或更多级的访问赋值会容易导致数据逃逸 。这里加上容易二字是因为随着语言的发展,相信这些问题会被慢慢解决,但现阶段 , 这个可以作为我们分析逃逸现象的依据 。
下面代码中包含 2 种很常规的写法,但他们却有着很大的性能差距,建议自己想下为什么 。
Benchmark 和 pprof 给出的结果:
熟悉堆栈概念可以让我们更容易看透 Go 程序的性能问题,并进行优化 。
多级间接赋值会导致 Go 编译器出现不必要的逃逸,在一些情况下可能我们只需要修改一下数据结构就会使性能有大幅提升 。这也是很多人不推荐在 Go 中使用指针的原因,因为它会增加一级访问路径,而map,slice,interface{}等类型是不可避免要用到的,为了减少不必要的逃逸,只能拿指针开刀了 。
大多数情况下,性能优化都会为程序带来一定的复杂度 。建议实际项目中还是怎么方便怎么写,功能完成后通过性能分析找到瓶颈所在 , 再对局部进行优化 。
什么是逃逸分析?在C语言中go语言机制和逃逸,可以使用malloc和free手动在堆上分配和回收内存 。Go语言中,堆内存是通过垃圾回收机制自动管理go语言机制和逃逸的,无需开发者指定 。那么,Go编译器怎么知道某个变量需要分配在栈上,还是堆上呢go语言机制和逃逸?编译器决定内存分配位置的方式,就称之为逃逸分析(escape analysis) 。逃逸分析由编译器完成,作用于编译阶段 。
【golang】内存逃逸常见情况和避免方式因为如果变量的内存发生逃逸,它的生命周期就是不可知的 , 其会被分配到堆上 , 而堆上分配内存不能像栈一样会自动释放,为了解放程序员双手 , 专注于业务的实现 , go实现了gc垃圾回收机制,但gc会影响程序运行性能,所以要尽量减少程序的gc操作 。
【go语言机制和逃逸 go语言内存逃逸】 1、在方法内把局部变量指针返回,被外部引用,其生命周期大于栈,则溢出 。
2、发送指针或带有指针的值到channel,因为编译时候无法知道那个goroutine会在channel接受数据,编译器无法知道什么时候释放 。
3、在一个切片上存储指针或带指针的值 。比如[]*string , 导致切片内容逃逸,其引用值一直在堆上 。
4、因为切片的append导致超出容量,切片重新分配地址 , 切片背后的存储基于运行时的数据进行扩充,就会在堆上分配 。
5、在interface类型上调用方法,在Interface调用方法是动态调度的 , 只有在运行时才知道 。

推荐阅读