go的逃逸分析机制

96 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

什么是逃逸分析

逃逸分析就是分析一个变量是否被引用,然后决定一个变量是分配在一个栈上还是一个堆上

什么是栈和堆

在计算机系统中,栈和堆都是操作系统对内存管理的两种方式。

栈的内存分配速度很快,而且会自动被释放,效率很高。

但是堆的分配速度比较慢,它需要先找到一块内存空间,而且不可以自动释放资源,在go中回收是由垃圾回收机制完成的。

优缺点分析

不管如何栈和堆对于一个程序来说都是储存变量的一个空间。

栈的优点,它是硬件支持的,入栈出栈很快,效率高。那么它就是和储存一些过期时间短、作用域小的变量,比如局部变量。

而堆呢,它的分配速度很慢,就可以储存一些过期时间长、作用域大的变量,比如全局变量。

go的逃逸机制

那么go的逃逸机制设计就是由一个点决定的---就是一个变量是否会被外部引用。

  • 如果变量在函数外部没用被引用,那么他就会优先被分配在栈上

  • 如果变量在函数外部被引用了,那么它就会一定被放到堆上

  • 如果栈上放不下,就一定放在堆上

go常见的内存逃逸场景

  • 返回局部变量指针,因为指针的原因,对象的内存不会随着函数的结束而被回收
  • 栈空间不足,比如创建了一个超长的切片
  • 动态类型逃逸,创建的时候数据类型使用了interface{},编译期间很难确定使用的类型,也会发生逃逸
  • 闭包引用导致逃逸分析
func getSum()func ()int{
    a := 1
    return func()int{
        a++
        return a
    }
}

传值真的比传指针效率高吗?

如果拷贝的数据量小,指针可能会发生逃逸,会使用堆,增加GC的负担。所以指针传递效率不一定是最高的

go和c/c++的栈堆概念

  • 在c/c++中栈和堆都是操作系统层级上的概念,是关联硬件的,是由操作系统和编译器共同决定的

  • go中的栈和堆却都是逻辑上的概念(类似数据结构)。操作系统层级上的栈都被调度器、垃圾回收、系统调用等机制消耗了。而我们代码中使用的栈和堆其实是从操作系统申请的一块堆内存模拟出来的,是一种逻辑上的概念。

  • 也因此,c/c++的栈很小。而go的'栈'可以开很大,比如go的一个协程就是一个栈,通常是2或4kb,但可以开成千上万个协程。

  • 而且go有时候为了防止内存碎片化,会对整个栈就行内存迁移,然后拷贝数据(切片扩容机制,当容量大于原数组容量,就会进行迁移拷贝)。所以指针有时候是会变的,只有指针指向的值有意义。因此,go禁止指针的算术运算