安得倚天抽宝剑——Go中new到底在堆还是栈中分配

·  阅读 1316
安得倚天抽宝剑——Go中new到底在堆还是栈中分配

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情

本文收录于我的专栏:《让我们一起Golang》

安得倚天抽宝剑——Go中new到底在堆还是栈中分配

逃逸分析是什么?

在C/C++中,我们是使用malloc或new来从堆山取一块内存,怎么使用这块内存,完全取决于程序员,因此很容易发生内存泄漏。而Go语言会在两个地方给变量分配内存,虽然Go也是可以通过new来给变量分配内存,但是分配的这块内存,可能在堆上,也可能在栈上。从性能的角度出发,在栈上分配内存和在堆上分配内存,性能差异是非常大的。因此一个变量是在堆上分配内存,还是在栈上分配内存,是需要编译器经过逃逸分析才能得出结论。

在编译原理中,分析指针动态范围的方法称为逃逸分析。当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。

ab654dcf5c4740bcccd8e77bba2c0e23_202208251401387.png

逃逸分析存在的意义何在?

其实逃逸分析并不是专属于Go的“蜜糖”,Java也使用逃逸分析。

但是Java的逃逸分析有很多限制,对于不进行全局转义的对象,Java不会将堆分配替换为堆栈分配。然而,Java使用了另一种称为标量替换的技巧,它避免了将对象放在栈上的需要。本质上,它分解对象,并将其基本成员放在栈上。

Go的GC,让程序员可以不理解堆和栈也可以编写高效的业务,让编译器去和堆和栈打交道就行了...

使用逃逸分析后,如果编译器发现这个变量在该函数结束后不会再调用了,就会把这个变量分配到栈上,毕竟使用栈速度快、不会产生内存碎片。如果编译器发现某个变量在函数之外还有其他地方要引用,那么就把这个变量分配到堆上。

为什么不将变量全部分配到堆空间上呢?像C那样不是也挺好的吗?

这是因为堆不能像栈那样函数一结束就自动清理,会导致GC频繁工作,而通过逃逸分析,我们可以尽可能把变量分配到栈上,可以减少内存碎片,减少GC回收的时间,所以逃逸分析是Go用来减少GC压力的一个技巧。

image.png

Golang的逃逸分析怎么实现,方法和操作是怎样?

Golang的逃逸分析简单来说就是,如果一个变量的引用从声明它的函数中返出去了,则发生“逃逸”,因为它有可能在函数外被别的内容使用,所以必须分配到堆上。如果变量在函数外部没有被引用,那么就优先将这个变量放置在栈上。不过有如下几种特殊情况:

  • 函数调用其他函数
  • 引用作为结构体的成员变量
  • 切片和映射
  • Cgo指向变量的指针

还有一种情况,如果栈已经满了,或者放不下,自然只能放在堆上了。

参考资料

机械工业出版社《Go程序员面试笔试宝典》

Go并不需要Java风格的GC robberphex.com/go-does-not…

详解二:Go 语言机制之逃逸分析 zhuanlan.zhihu.com/p/137536970

Golang逃逸分析 www.jianshu.com/p/670ba8ed0…

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改