栈内存&堆内存&逃逸分析

1. 栈内存

栈内存由编译器自动分配和释放,开发者无法控制。栈内存一般存储函数中的局部变量、参数等,函数创建的时候,这些内存会被自动创建;函数返回的时候,这些内存会被自动释放。

2. 堆内存

堆分配内存和栈分配内存相比,堆适合不可预知大小的内存分配。但是为此付出的代价是分配速度较慢,而且会形成内存碎片 。

堆内存的生命周期比栈内存要长,如果函数返回的值还会在其他地方使用,那么这个值就会被编译器自动分配到堆上。堆内存相比栈内存来说,不能自动被编译器释放,只能通过垃圾回收器才能释放,所以栈内存效率会很高。

3. 逃逸分析

栈内存的效率更高,肯定是优先使用栈内存。那么 Go 语言是如何判断一个变量应该分配到堆上还是栈上的呢?这就需要逃逸分析了。

package main

func main() {
    newString()
}

func newString() *string{
   s:=new(string)
   *s = "wohu"
   return s   //返回指针,发生逃逸
}
go build -gcflags="-m -l" demo.go
# -m 表示打印出逃逸分析信息;
# -l 表示禁止内联,可以更好地观察逃逸;

指针作为函数返回值的时候,一定会发生逃逸。

逃逸到堆内存的变量不能马上被回收,只能通过垃圾回收标记清除,增加了垃圾回收的压力,所以要尽可能地避免逃逸,让变量分配在栈内存上,这样函数返回时就可以回收资源,提升效率。

要学会重用内存,比如使用 sync.Pool

func newString() string{
   s:=new(string)
   *s = "wohu"
   return *s   //返回数值,没有发送逃逸
}
0%