javascript – GO – 转义分析

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

JavaScript / Python中,只有闭包变量位于堆中,因为它们必须超出函数调用,它们才会被创建.

在GO中,一些GO类型(如slice type [] int)确实引用了内存的其他部分,如JavaScript / Python.

在GO中,并非所有类型的变量都包含引用,例如Javascript / Python.

例如,

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

2)int类型变量c直接存储int值,如C,除了C,通过提供语法(& c)来获得位置访问权限,从而提供更多控制.

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

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是否在堆中分配?

最佳答案 在Go中,您应该信任编译器以做出最佳决策.如果可能的话,它将在堆栈上分配内存.另见
the FAQ

From a correctness standpoint, you don’t need to know. Each variable in Go exists as long as there are references to it. The storage location chosen by the implementation is irrelevant to the semantics of the language.

The storage location does have an effect on writing efficient programs. When possible, the Go compilers will allocate variables that are local to a function in that function’s stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.

In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.

没有优化(内联),是的,将在堆中分配.我们可以通过传递-gcflags =’ – m'(https://play.golang.org/p/l3cZFK5QHO)来检查逃逸分析:

$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  }
$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在主goroutine的堆栈上分配,而变量b在堆上分配.正如我们从输出中看到的那样,b的地址在0x1043xxxx上,而其他所有在0x1042xxxx上.

$./1
[5/5]0x10432020
[5/5]0x10429f58
[5/5]0x10429f44
0x10429f40
点赞