美文网首页
Go语言逃逸分析

Go语言逃逸分析

作者: 我思故我在2020 | 来源:发表于2021-01-18 15:16 被阅读0次

    本贴转自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语言逃逸分析

          本文链接:https://www.haomeiwen.com/subject/hjrxzktx.html