在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
楔子对于像C这样的语言,不同位置的变量应该申请在内存的哪个区都是很固定的,比如:全局变量会在全局区创建,函数里面的变量会在栈区创建,并且我们还可以手动地从堆区申请内存、手动地释放内存。但是到了go语言中,这些都不需要我们管了,我们不需要关心变量到底申请在哪个区,编译器的垃圾回收机制会自动帮我们判断创建的变量到底应该分配到哪个区。可是go的编译器是如何得知的呢?比如这样一个例子:
这段代码表示接收两个整型,然后相加用变量存储起来、返回变量的指针。这段代码在C程序员的眼中肯定是有问题的,但是在go语言中这是完全正常的代码。因为这个变量 什么是逃逸分析?逃逸分析:通过指针的动态范围决定一个变量究竟是分配在栈上还是应该分配在堆上。 我们知道栈区是可以自动清理的,所以栈区的效率很高,但是不可能把所有的对象都申请在栈上面,而且栈空间也是有限的。但如果所有的对象都分配在堆区的话,堆又不像栈那样可以自动清理,因此会频繁造成垃圾回收,从而降低运行效率。这里多提一句,Python中的对象都是分配在堆上的,Python中的对象本质上就是c中malloc在堆上申请的一块内存,尽管Python通过内存池降低了频繁和操作系统交互,但还是架不住效率低。所以在go中,会通过逃逸分析,把那些一次性的对象分配到栈区,如果后续还有变量指向,那么就放到堆区。 逃逸分析的标准首先可以肯定的是,如果函数里面的变量返回了一个地址,那么这个变量肯定会发生逃逸。go编译器会判断变量的生命周期,如果编译器认为函数结束后,这个变量不再被外部的引用了,会分配到栈,否则分配到堆。
因此有两个结论:
逃逸分析演示
我们看到变量c发生了逃逸,这和我们想的一样,但是为什么main函数里面的p居然也逃逸了。因为编译期间不确定变量类型的话,那么也会发生逃逸。除此之外,还可以通过反汇编命令来查看: 总结堆上动态内存分配的开销比栈要大很多,所以有时我们传递值比传递指针更有效率。因为复制是栈上完成的操作,开销要比变量逃逸到堆上再分配内存要少的多,比如说:
因为func2里面接收一个指针,所以func1里面的c变量毫无疑问会逃逸到堆、在堆上分配;而如果func2接收不是指针,那么c变量会直接栈上分配,然后只需要拷贝一个值即可;因为是栈,所以拷贝值的效率反而会更高一些,要是逃逸到堆、再分配的话,效率反而更低。因此根据场景具体分析,到底函数要不要接受指针,总之最好学会擅用go语言的逃逸分析。当然我们不需要知道编译器的逃逸分析规则,只需要观察程序的运行情况就行了。 |
请发表评论