Go逃逸分析
Last Update:
Word Count:
Read Time:
Golang 逃逸学习
golang的内存管理比较安全,不可直接操作内存,且在编译期间会进行数组越界检查,在运行的时候直接报错。
这篇文章就记录一下golang逃逸的一些相关原理吧。
golang逃逸分析wiki
In compiler optimization, escape analysis is a method for determining the dynamic scope of pointers - where in the program a pointer can be accessed. It is related to pointer analysis and shape analysis.
When a variable (or an object) is allocated in a subroutine, a pointer to the variable can escape to other threads of execution, or to calling subroutines. If an implementation uses tail call optimization (usually required for functional languages), objects may also be seen as escaping to called subroutines. If a language supports first-class continuations (as do Scheme and Standard ML of New Jersey), portions of the call stack may also escape.
If a subroutine allocates an object and returns a pointer to it, the object can be accessed from undetermined places in the program — the pointer has “escaped”. Pointers can also escape if they are stored in global variables or other data structures that, in turn, escape the current procedure.
Escape analysis determines all the places where a pointer can be stored and whether the lifetime of the pointer can be proven to be restricted only to the current procedure and/or thread
逃逸分析优势
1 最大的好处应该是减少gc的压力,不逃逸的对象分配在栈上,当函数返回时就回收了资源,不需要gc标记清除。
2 因为逃逸分析完后可以确定哪些变量可以分配在栈上,栈的分配比堆快,性能好
3 同步消除,如果你定义的对象的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。
go与c/c++编译的程序区别
c/c++编译的程序,堆栈空间给的比较少,一般做大型项目的时候,数据量大了就把数据放在堆里储存,而go呢,内存机制有自己本身管理,采用堆栈迁移的方式把堆栈迁移到所开辟出来空间比较大的地方,所以golang 在运行完大多数对象都可以放在堆栈中。下面来跟踪一下golang程序的运行
1 |
|
以上编译为elf文件后是gdb进行调试
main_mian的地址为0x45DA40
程序刚开始运行时的堆栈就为操作系统所给的堆栈空间
1 |
|
可以看到以上,rsp与rbp已经不再是0x7f开头的地址了而是进行了堆栈迁移
1 |
|
可以发现新的堆栈空间大小为0x4000000, 而操作系统所给的为0x21000, 所以golang的变量大部分都会优先储存在堆栈上,因为堆栈空间比较大,且内存管理比较简单。
go的逃逸分析
go在动态编译的时候进行逃逸分析,来决定一个对象放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上。
开启逃逸分析日志
在编译的时候参数加上-gcflags '-m'
,为了不产生inline函数,一般都会加上-l
也就是 -gcflags '-m -l'
Example 1
1 |
|
逃逸分析
1 |
|
Example 2
1 |
|
输出
1 |
|
x没有被引用,没有发生逃逸
Example 3
1 |
|
输出
1 |
|
go进行值传递,而在调用ref后进行引用,避免内存错误,则会将z放在heap上。
Example 4
1 |
|
输出
1 |
|
对y进行了值引用,则使y放在heap上
Example 5
1 |
|
输出
1 |
|
对原先堆栈里的数据进行引用,没有发生逃逸
Example 6
1 |
|
输出
1 |
|
z没有逃逸,有两个指针指向i变量,而i逃逸了,go的逃逸分析不知道z和i的关系,逃逸分析不知道参数y是z的一个成员,所以只能把i分配给堆管理
总结
以上是golang编译器通过分析代码会在编译时觉得哪些变量该分配在stack中,哪些变量该分配在heap中。
ref: https://www.bookstack.cn/read/For-learning-Go-Tutorial/src-chapter13-01.0.md