Каково значение вывода "go run -gcflags -m xxx.go"

Попытка проверить, размещена ли локальная переменная в куче или стеке в программе 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зачем это было?

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() не будет возможности добраться до вашего y локальная переменная.

Совет:

Вы можете получить более подробную информацию об оптимизации решений и избежать анализа, передав -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
Другие вопросы по тегам