首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >逃逸分析

逃逸分析
EN

Stack Overflow用户
提问于 2017-01-28 08:08:21
回答 1查看 1.8K关注 0票数 5

在许多语言中,局部变量位于调用堆栈中。

在JavaScript/Python中,只有闭包变量位于堆中,因为它们必须存在于函数调用之外,它们被创建。

在GO中,一些GO类型(如片类型[]int)引用内存的其他部分,比如JavaScript/Python。

在GO中,并非所有类型的变量都保存引用,比如Javascript/Python。

例如,

1) [3]int类型变量b直接存储int的数组,如C,但C允许使用C语法&b[index]访问每个数组元素的位置,以获得更多的控制。

2) int类型变量c直接存储一个int值,比如C,但C通过提供语法(&c)来获得位置访问来提供更多的控制。

在GO中,我的理解是,对于堆/堆栈上的局部变量,取决于在示例代码中应用编译器的转义分析(下面),

代码语言:javascript
复制
func foo() []int {
  // the array lives beyond the call to foo in which it is created
  var a [5]int
  return a[:] // range operator
}

这告诉编译器变量a的存在超出了它的范围,所以在堆中分配,而不是堆栈。

问题:

变量a是否在堆中分配?

EN

回答 1

Stack Overflow用户

发布于 2017-01-28 08:49:36

在Go中,您应该相信编译器会做出最好的决定。如果可能的话,它将在堆栈上分配内存。另见常见问题

从正确的角度来看,你不需要知道。Go中的每个变量只要有引用就存在。实现选择的存储位置与语言的语义无关。 存储位置确实对编写高效的程序有影响。在可能的情况下,Go编译器将为函数的堆栈框架中的函数分配本地变量。但是,如果编译器不能证明函数返回后没有引用变量,那么编译器必须在垃圾收集堆上分配变量,以避免出现悬空指针错误。另外,如果局部变量非常大,那么将其存储在堆上而不是堆栈上可能更有意义。 在当前编译器中,如果使用了变量的地址,则该变量是堆上分配的候选变量。但是,一个基本的转义分析可以识别这样一些情况,即这些变量不会活过函数的返回,并且可以驻留在堆栈中。

如果没有优化(内联),将在堆a中分配yes 。我们可以通过传递-gcflags='-m' (https://play.golang.org/p/l3cZFK5QHO)来检查转义分析:

代码语言:javascript
复制
$ nl -ba 1.go
     1  package main
     2  
     3  func inlined() []int {
     4      var a [5]int
     5      return a[:]
     6  }
     7  
     8  //go:noinline
     9  func no_inline() []int {
    10      var b [5]int
    11      return b[:]
    12  }
    13  
    14  func main() {
    15      var local_array [5]int
    16      var local_var int
    17      println(no_inline())
    18      println(inlined())
    19      println(local_array[:])
    20      println(&local_var)
    21  }
代码语言:javascript
复制
$ go build -gcflags='-m' 1.go
# command-line-arguments
./1.go:3: can inline inlined
./1.go:18: inlining call to inlined
./1.go:5: a escapes to heap
./1.go:4: moved to heap: a
./1.go:11: b escapes to heap
./1.go:10: moved to heap: b
./1.go:18: main a does not escape
./1.go:19: main local_array does not escape
./1.go:20: main &local_var does not escape

我们看到编译器决定在第5行分配inlined.a,在堆上分配第10行的no_inline.b,因为它们都逃避了它们的作用域。

但是,在内联之后,编译器注意到a不再转义,因此它决定可以再次在堆栈上分配变量(第18行)。

结果是,变量a被分配到main goroutine的堆栈上,而变量b被分配到堆上。从输出中可以看到,b的地址位于0x1043xxxx,而其他所有地址都位于0x1042xxxx。

代码语言:javascript
复制
$ ./1
[5/5]0x10432020
[5/5]0x10429f58
[5/5]0x10429f44
0x10429f40
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41907662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档