Golang逃逸分析

bytemode · · 1303 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

## Golang逃逸分析 ### 为什么需要逃逸分析 通常在编译代码的时候,编译器根据分析,判断将变量分配在栈或堆上。函数定义中,一般将局部变量和参数分配到栈上(stack frame)上。但是,如果编译器不能确定在函数返回(return)时,变量是否被引用(reference),分配到堆上;如果局部变量非常大,也应分配在堆上。 如果对变量取地址(*和&操作),则有可能分配在堆上。此外,还需要进行逃逸分析(escape analytic),判断return后变量是否被引用,不引用分配到栈上,引用分配到堆上。 在golang中逃逸分析是一种确定指针动态范围的方法,可以分析在程序的哪些地方可以访问到指针. golang 编译器会将函数的局部变量分配到函数栈帧(stack frame)上。 然而,如果编译器不能确保变量在函数 return之后不再被引用,编译器就会将变量分配到堆上。而且,如果一个局部变量非常大,那么它也应该被分配到堆上而不是栈上。 当前情况下,如果一个变量被取地址,那么它就有可能被分配到堆上。然而,还要对这些变量做逃逸分析,如果函数return之后,变量不再被引用,则将其分配到栈上。 其实在golang中所有静态内存的其实分配都是在 stack 上进行的,而函数体在执行结束出栈后所有在栈上分配的内存都将得到释放,如果此时直接返回当前作用域变量的指针,这在下层函数的寻址行为就会因为出栈的内存释放而造成空指针异常。这个时候我们就得需要用到malloc在堆上(heap)动态分配内存,自己管理内存的生命周期,自己手动释放才是安全的方式。 然而escape analysis的存在让go完美规避了这些问题,编译器在编译时对代码做了分析,如果发现当前作用域的变量没有超出函数范围,则会自动在stack上分配,如果找不到了,则会在heap上分配。这样其实开发者就不用太关心堆栈的使用边界,在代码层面上完全不需要关心内存的分配,把底层要考虑的问题交给编译器,同时也减小了gc回收的压力。 go在一定程度消除了堆和栈的区别,因为go在编译的时候进行逃逸分析,来决定一个对象放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上。 ``` package main import () func foo() *int { var x int return &x } func bar() int { x := new(int) *x = 1 return *x } func main() {} ``` 运行后 : ``` > go run -gcflags '-m -l' escape.go ./main.go:6: moved to heap: x ./main.go:7: &x escape to heap ./main.go:11: bar new(int) does not escape ``` foo() 中的 x 最后在堆上分配,而 bar() 中的 x 最后分配在了栈上 ### 逃逸分析的作用 1. 逃逸分析的好处是为了减少gc的压力,不逃逸的对象分配在栈上,当函数返回时就回收了资源,不需要gc标记清除。 2. 逃逸分析完后可以确定哪些变量可以分配在栈上,栈的分配比堆快,性能好 3. 同步消除,如果你定义的对象的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。 ### 什么时候发生逃逸 通常在go中函数都是运行在栈上的,在栈声明临时变量分配内存,函数运行完毕在回收该段栈空间,并且每个函数的栈空间都是独立的,不能被访问到的。但是在某些情况下,栈上的空间需要在该函数被释放后依旧能访问到,这时候就涉及到内存的逃逸了

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1303 次点击  
加入收藏 微博
下一篇:chan基本用法
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传