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

2023-12-01

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


Code

变量_堆_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()
}

Execute:

去运行-gcflags -mvariable_heap_stack.go

Output:

# 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,这意味着移动到堆,对吗?和上面的有什么区别呢?
  • The y变量是局部的,函数返回后没有人引用它,但仍然有一行y escapes to heap,这是为什么呢?

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

这意味着消息中指示的值离开“界限”函数的内部,因此,不能保证函数外部会发生什么,因此如果该值是指针或引用(但仅在此时),则必须在堆上分配指向或引用的值。

你可以想到escapes to heap作为调试消息,它并不表明您的变量之一已“重新定位”到堆。

简单来说,“逃逸到堆”类似于以下术语:“它离开了功能”, or “它在函数之外传递”.

作为示例这一行:

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

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

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

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

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

请注意,自从fmt.Printf()期望类型的值...any,变量y将被包装在接口值中,然后在切片中,并将其包装在接口值中是导致“转义”的原因。如果你将它传递给一个期望的函数int甚至...int,不会发生任何逃逸,因为这只会通过int值没有任何参考或连接y任何变量。

Tip:

您可以通过传递获得有关优化决策和逃逸分析的更多详细信息-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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐