逃逸分析 | 青训营笔记

229 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记。逃逸分析是Go语言里比较有特色的基础知识。

逃逸分析

1.堆栈

在C和C++中,程序的堆栈本质上是操作系统层级的概念。在程序启动时,一般情况下OS会维护一个消耗内存的空间,并且将其在逻辑上划分为堆内存和栈内存。堆作为程序向OS申请内存时分配使用,栈指程序运行时自动获得的一小块内存。而在Go语言中,由于调度器、垃圾回收等各个组件的使用和协调的消耗,传统的栈基本被全部消耗了,所以Go中的堆栈相对模糊,是建立在堆上的逻辑堆和栈。

2.逃逸

为了提高效率,减少构造函数所带来的开销,很多时候函数等在返回时选择返回指针。但是问题同时也就出现了,函数内部定义的局部变量由于在栈上,会在函数结束时销毁,但此时其指针作为返回值返回,外部进行操作时会扰乱程序的运行甚至导致崩溃。

//C++
int* f() {
    int* t = new int;
    *t = 3;
    return t;
}

上面代码在C++中可以将变量在堆上创建,但外部调用很可能忘记删除而产生内存泄漏。

//Go
func f() *int {
    t := new(int)
    *t = 3
    return t
}

而在Go中,却没有任何问题,这就是因为编译器进行了逃逸分析,即在编译器执行静态代码分析后,对内存管理进行优化和简化。

3.GO中逃逸分析的规则

Go语言中逃逸分析有个基本原则,一旦一个函数返回对一个变量的引用,变量就会发生逃逸。逃逸分析是由编译器分析代码来决定,而编译器的规则主要是根据变量是否被外部引用来决定是否逃逸。主要为两种情况:a.变量在函数外部没有引用,则优先到栈上(除非需要申请的内存过大超过了栈的存储能力还是会到堆)。b.变量在函数外部存在引用,则必定到堆上。