代码之家  ›  专栏  ›  技术社区  ›  Eric

“go run-gcflags-m xxx.go”的输出是什么意思

  •  1
  • Eric  · 技术社区  · 6 年前

    尝试检查go程序中的堆或堆栈上是否分配了局部变量,但无法确定go的gc的某些输出的含义。


    代码

    变量堆堆栈.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 ,为什么?
    1 回复  |  直到 6 年前
        1
  •  1
  •   icza    6 年前

    做什么 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 是一个直接的声明,您的一个变量确实被“重新定位”到堆中。 注意:“relocated”意味着变量将首先分配到堆上,实际的“relocated”在任何情况下都不会发生。

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

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

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

    是的 不会因为这个而被移动到堆中,没有必要,因为它被传递到 fmt.Printf() 通过复制其价值 fmt.Printf() 将无法联系到您的 是的 局部变量。

    小贴士

    您可以通过 -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