Go语言逃逸分析

本贴转自https://www.cnblogs.com/itbsl/p/10476674.html
如侵权,请联系QQ359152155删除。

Go变量逃逸分析

逃逸分析 决定一个变量是分配在堆上还是栈上。

逃逸分析这种操作把变量合理地分配到它该去的地方,即使你是用new申请到的内存,如果我发现你竟然在退出函数后就没有用了,那么就把你丢到栈上,毕竟栈上的内存分配比堆上快得多;反之,即使你表面上只是一个普通的变量,但是经过逃逸分析后发现在退出函数之后还有其他地方在引用,那我就把你分配到堆上。

如果变量都分配到堆上,堆不像栈可以自动清理,它会引起Go频繁地进行垃圾回收,而垃圾回收会占用比较大的系统开销。

通过逃逸分析,可以尽量把哪些不需要分配到对上的变量直接分配到栈上,堆上的变量少了,会减轻分配堆内存的开销,同时也会减少GC的压力,提高程序的运行速度。

逃逸分析是怎么完成的

Go逃逸分析的最基本原则是:如果一个函数返回堆一个变量的引用,那么它就会发生逃逸。

编译器会分析代码的特征和代码生命周期,Go中的变量只有在编译器可以证明在函数返回后不会再被引用,才会分配到栈上,其他情况下都是分配到堆上。

逃逸分析实例

Go提供了相关的命令,可以查看变量是否发生逃逸。

用下面这个例子:

package main

import "fmt"

func foo() *int {
    t := 3
    return &t;
}

func main() {
    x := foo()
    fmt.Println(*x)
}

foo函数返回一个局部变量的指针,main函数里变量x接收它。执行如下命令:

go build -gcflags="-m -l" main.go

-l是为了不让foo函数被内联,得到如下输出:

# command-line-arguments
.\hello.go:6:5: moved to heap: t
.\hello.go:12:16: ... argument does not escape
.\hello.go:12:17: *x escapes to heap

foo函数里的变量t逃逸了,和我们预想的一致。让我们不解的是为什么main函数里的*x也逃逸了?这是因为fmt.Println()函数的参数是interface类型,编译期间很难确定其参数的具体类型,也会发生逃逸。

总结

堆上动态分配内存比栈上静态分配内存,开销大很多。

Go编译器会在编译器分析变量的生命周期,编译器会根据变量是否在函数返回后仍旧被引用来决定是否逃逸。对于Go程序原来说,编译器的这些逃逸分析规则不需要掌握,我们只需通过`go build -gcflags="-m"命令来观察变量逃逸情况就行了。

不要盲目使用变量的指针作为函数参数,虽然它会减少复制操作,但其实当参数为变量自身的时候,复制是在站上完成的操作,开销远比变量逃逸后动态地在堆上分配内存少的多。

最后,尽量写出少一些逃逸的代码,提升程序的运行效率。

你可能感兴趣的:(Go语言逃逸分析)