【问题标题】:What is the meaning of the output from 'go run -gcflags -m xxx.go''go run -gcflags -m xxx.go' 的输出是什么意思
【发布时间】:2018-07-25 12:20:04
【问题描述】:

在go程序中试图检查一个局部变量是分配在堆上还是栈上,不能确定go的gc的一些输出的含义。


代码

variable_heap_stack.go:

// variable heap & stack learn,
// run with:
//  go run -gcflags -m xxx.go
package main

import "fmt"

func getPointerOfLocalVar() *int {
    x := 10 // go will put it into heap,
    return &x
}

// heap & stack test,
func heapStackTest() {
    px := getPointerOfLocalVar()
    fmt.Printf("x: %d\n", *px)

    y := 20 // go will put it into stack,
    fmt.Printf("y: %d\n", y)
}

func main() {
    heapStackTest()
}

执行:

运行 -gcflags -m variable_heap_stack.go

输出:

# command-line-arguments
./variable_heap_stack.go:8:6: can inline getPointerOfLocalVar
./variable_heap_stack.go:15:28: inlining call to getPointerOfLocalVar
./variable_heap_stack.go:10:9: &x escapes to heap
./variable_heap_stack.go:9:6: moved to heap: x
./variable_heap_stack.go:16:24: *px escapes to heap
./variable_heap_stack.go:19:13: y escapes to heap
./variable_heap_stack.go:15:28: heapStackTest &x does not escape
./variable_heap_stack.go:16:12: heapStackTest ... argument does not escape
./variable_heap_stack.go:19:12: heapStackTest ... argument does not escape
x: 10
y: 20

问题

  • escapes to heap 是什么意思?它会堆还是不堆?
  • moved to heap,这意味着移动到堆,对吧?和上面的有什么区别?
  • y变量是本地变量,函数返回后没有人引用它,但还是有一行y escapes to heap,这是为什么呢?

【问题讨论】:

    标签: go compiler-optimization escape-analysis


    【解决方案1】:

    escapes to heap 是什么意思?它会堆还是不堆?

    这意味着消息中指示的值离开了函数的“边界”,因此无法保证它在函数之外会发生什么,所以如果该值是指针或引用(但仅限于此),指向或引用的值必须在堆上分配。

    您可以将escapes to heap 视为一条调试消息,它并不表示您的某个变量已“重新定位”到堆中。

    所以说的简单点,“转义到堆”类似于这个词:“它离开函数”,或者“它被传递到外面函数”.

    以这一行为例:

    ./variable_heap_stack.go:16:24: *px escapes to heap
    

    表示值 *px 被传递到函数外部,即在此行中作为 fmt.Printf() 的参数:

    fmt.Printf("x: %d\n", *px)
    

    moved to heap,这意味着移动到堆,对吧?和上面的有什么区别?

    这表明编译器决定将消息中指示的变量移动到堆中,因为它可能在函数之外被引用,因此它必须在函数中存在。并且由于从函数返回后堆栈分配的值可能会变得无效,因此要使指示的变量在函数返回后有效,它必须在堆上。

    Moved to heap 是直接声明您的一个变量确实“重新定位”到了堆中。 注意:“重新定位”意味着变量将首先分配到堆上,实际的“搬迁”无论如何都不会发生。

    y变量是局部变量,函数返回后没有人引用它,但还是有一行y escapes to heap,这是为什么呢?

    如前所述,这并不意味着y被重定位到堆中,它只是意味着y的值被传递到函数之外,即作为fmt.Printf()的参数在这一行:

    fmt.Printf("y: %d\n", y)
    

    y 不会因此而被移动到堆中,没有必要,因为它通过复制其值传递给fmt.Printf(),而fmt.Printf() 将无法到达您的@ 987654338@局部变量。

    提示:

    您可以通过两次传递-m 来获取有关优化决策和逃逸分析的更多详细信息,如下所示:

    go run -gcflags='-m -m' variable_heap_stack.go
    

    那么这个命令的输出将是:

    ./variable_heap_stack.go:8:6: can inline getPointerOfLocalVar as: func() *int { x := 10; return &x }
    ./variable_heap_stack.go:14:6: cannot inline heapStackTest: non-leaf function
    ./variable_heap_stack.go:15:28: inlining call to getPointerOfLocalVar func() *int { x := 10; return &x }
    ./variable_heap_stack.go:22:6: cannot inline main: non-leaf function
    ./variable_heap_stack.go:10:9: &x escapes to heap
    ./variable_heap_stack.go:10:9:         from ~r0 (return) at ./variable_heap_stack.go:10:2
    ./variable_heap_stack.go:9:2: moved to heap: x
    ./variable_heap_stack.go:16:24: *px escapes to heap
    ./variable_heap_stack.go:16:24:        from ... argument (arg to ...) at ./variable_heap_stack.go:16:12
    ./variable_heap_stack.go:16:24:        from *(... argument) (indirection) at ./variable_heap_stack.go:16:12
    ./variable_heap_stack.go:16:24:        from ... argument (passed to call[argument content escapes]) at ./variable_heap_stack.go:16:12
    ./variable_heap_stack.go:19:13: y escapes to heap
    ./variable_heap_stack.go:19:13:        from ... argument (arg to ...) at ./variable_heap_stack.go:19:12
    ./variable_heap_stack.go:19:13:        from *(... argument) (indirection) at ./variable_heap_stack.go:19:12
    ./variable_heap_stack.go:19:13:        from ... argument (passed to call[argument content escapes]) at ./variable_heap_stack.go:19:12
    ./variable_heap_stack.go:15:28: heapStackTest &x does not escape
    ./variable_heap_stack.go:16:12: heapStackTest ... argument does not escape
    ./variable_heap_stack.go:19:12: heapStackTest ... argument does not escape
    x: 10
    y: 20
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      • 2015-11-03
      • 1970-01-01
      • 2018-05-07
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多