javascript - 逃逸分析

标签 javascript python c go escape-analysis

在很多语言中,局部变量都位于调用栈中

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


在 GO 中,一些 GO 类型(如 slice 类型 []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.


如果不进行优化(内联),是的a会被分配到堆中。我们可以通过传递 -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分配在main goroutine的栈上,而变量b分配在堆上。正如我们从输出中看到的,b 的地址在 0x1043xxxx 上,而所有其他地址都在 0x1042xxxx 上。

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

关于javascript - 逃逸分析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41907662/

相关文章:

python - 从 html 页面分析和抓取链接

python - 如何在 Pandas 数据框中有效地查找交替 boolean 值的索引

Python:沿三次样条找到x(返回y),产生等距离的连续(x,y)对

指向结构的指针的 C typedef

c - 让程序在 C 中再次运行

javascript - 如何让 pdfJS 从 URL 渲染 pdf?

javascript - 什么时候 JavaScript 小到值得内联(以获得最佳性能)?

javascript - 在单个 prop React.js 中传递多个方法

c++ - 如何将 C 输出文件读入 Matlab

Javascript:首先按第二列desc排序二维数组,然后按第一列asc排序