Golang 内存逃逸分析

0x01 什么是逃逸

第一次听说逃逸是在雨痕学堂,一脸懵逼的百度了半天也没找到一个明确的说法,直到昨天在论坛上看到一篇关于变量逃逸的文章才明白。

因为函数都是运行在栈上的,在栈声明临时变量分配内存,函数运行完毕再回收该段栈空间,并且每个函数的栈空间都是独立的,其他代码都是不可访问的。但是在某些情况下,栈上的空间需要在该函数被释放后依旧能访问到,这时候就涉及到内存的逃逸了。

代码:

f1 和 f2 两个函数都是创建一个变量返回,不同的是 f1 返回变量副本,f2 返回变量指针。在大多数语言例如 C/C++,类似 f2 的函数是不对的,因为 d 是一个临时变量,return 过后就会被释放掉,返回毫无意义。但是在 golang 中,这种语法是允许的,它能正确的把 d 的地址返回到上层调用函数而不被释放。

正如上面所说,该函数在运行完毕后肯定是要释放的,内部分配的临时内存也要释放,所以 d 也应该被释放。而为了让 d 能被正确返回到上层调用,golang 采取了一种内存策略,把 d 从栈拿到堆的中去,此时 d 就不会跟随 f2 一同消亡了,这个过程就是一次逃逸。

0x02 编译报告

代码中两个函数上方的 go:noinline 注释是一个编译标记,让编译器不内联当前代码方便观察编译状态。因为这里代码十分简单,不给标记编译器会自动内联该段代码看不到效果。

在编译时可以通过 gcflags 选项带上-m 参数查看到编译状态,-m 一共可以携带四个:

通过第四行和第五行输出很直接就能看出,在代码的第 16 行,即函数 f2return 处,参数 d 产生了逃逸行为。

发表评论