首页 > 编程笔记 > Go语言笔记 阅读:1

Golang中的内存逃逸(附带实例)

一般函数内声明的局部变量应该存储在栈内存中,并且随着函数的调用和返回,该局部变量也会同步分配和释放。

然而,Go 语言稍有不同,因为 Go 语言存在内存逃逸情况,在某些情况下,局部变量也有可能存储在堆内存中。

为什么会有内存逃逸呢?举个例子,某个函数内部声明了一个局部变量,但是该函数返回了局部变量的地址。这种语法在其他语言,比如 C 语言,是不允许的,因为函数返回后,该局部变量的地址也会被释放。但是 Go 语言允许这种语法,只是这时候 Go 语言会将该局部变量存储在堆内存中,即该局部变量逃逸到了堆内存,如下所示:
package main
import "fmt"
func main() {
    ret := test()
    fmt.Println(ret)
}

func test() *int {
    var num = 10
    return &num
}

那我们怎么知道上面的程序发生了内存逃逸呢?实际上,在编译 Go 程序时,只需要添加一些编译参数,Go 语言编译器就会输出内存逃逸情况,如下所示:
// -N 禁止编译优化  -m 输出优化详情(包括逃逸分析)
// -m 可以多个,越多输出信息越多(最多4个)
go build -gcflags '-N -m -m -l' main.go
// 输出结果如下
# command-line-arguments
./main.go:8:6: num escapes to heap:
./main.go:8:6:    from &num (address-of) at ./main.go:9:9
./main.go:8:6:    from return &num (return) at ./main.go:9:2
./main.go:8:6:    moved to heap: num
./main.go:8:13: ... argument does not escape
从上面的结果可以很明显地看到,输出 num escapes to heap 说明变量 num 逃逸到堆内存了。

那还有哪些情况会引起内存逃逸呢?如果将一个局部变量的地址赋值给全局散列表或者切片,该局部变量也会逃逸到堆内存。再者,如果一个局部变量需要占用大量内存,这时候存储在栈内存是不是也就不太合适了,毕竟 Go 语言协程栈默认只有 2KB。当然还有其他情况,这里就不一一赘述了。

那平时开发 Go 程序时,需要关注内存逃逸情况吗?一般来说是不需要的,只是需要清楚一点:逃逸到堆内存上的变量如果不再使用,将会被垃圾回收功能自动回收。

相关文章