【Go语言基础2.1】变量逃逸

  编译器会优先将局部变量存放在栈中,便于及时清理,但是如果存放在栈上的变量被清理了,但是函数其他地方还存在对它的引用,这个时候就会发生不可知的错误。例如下面这段程序:

#Include
char *returnStr() {
    char p[] = "hello world";
    return p;
}

int main() {
    char *str;
    str = returnStr();
    printf("%s\n", str);
    return 0;
}

  由于"hello world"属于局部变量,会被存储在栈上,在returnStr函数退出之后,这个函数所占用的栈空间会被清空,这个局部变量也就不会存在,这时main函数再去获取这块内存存放的值就会发生异常。这种情况通常称为变量逃逸
  类似于上面的例子,一个对象的指针被其作用域之外的地方引用,我们就称这个变量发生了逃逸。
  这种情况对于使用C/C++的情况是经常发生的,然而Go语言对这种情况做了特殊处理,使得编程人员无需为这种事情过度担心。go语言通过编译器的逃逸分析,在执行静态代码分析时,对内存管理进行优化,将变量分配到合理的位置。

2、逃逸分析

观察下面这个例子:

package main

import "fmt"

func returnStr() (*string, *string) {
    strStack := "hello world!"
    strHeap := "hello world!"
    strStack2 := "hello world!"
    fmt.Println("strStack is: ", strStack, "strHeap is: ", strHeap, "strStack2 is: ", strStack2)
    return &strHeap, &strStack2
}

func main() {
    str,  _ := returnStr()
    fmt.Println(*str)
}

通过分析可以发现:
1、strStack这个变量在函数returnStr内部使用完成后,就没用了,这时可以把这个变量分配到栈上。
2、strStack2的地址被返回了,所以也会分配到堆上(这个地方和参考文章说的不同,经过多次实验,我觉得它还是会被分配到堆上)。
3、对于strHeap这个变量,在函数内部使用完之后,指针又被返回给main函数使用,那就把这个变量分配到堆上。

  如果将变量分配到堆上,可能会造成一定程序的性能损耗,因为不同于栈的自动释放,分配在堆上的变量,需要Go频繁地进行垃圾回收。通过逃逸分析,可以将变量在堆和栈上合理地进行分配,避免不必要的性能浪费。
  简单来说,编译器通过逃逸分析,分析出变量是否存在外部引用,判断变量存放的位置

  • 如果外部没有引用,优先存放到栈中
  • 如果外部存在应用,必然存放到堆中


    逃逸分析

参考文章:
Go变量逃逸分析

你可能感兴趣的:(【Go语言基础2.1】变量逃逸)