Вопрос по – Распределение стека и куча структур в Go и как они связаны со сборкой мусора

128

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

func myFunction() (*MyStructType, error) {
    var chunk *MyStructType = new(HeaderChunk)

    ...

    return chunk, nil
}


func myFunction() (*MyStructType, error) {
    var chunk MyStructType

    ...

    return &chunk, nil
}

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal allocates a fresh instance each time it is evaluated, so we can combine these last two lines.

http://golang.org/doc/effective_go.html#functions

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Ваш Ответ

4   ответа
17

В соответствии сПерейти на часто задаваемые вопросы:

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.

7

You don't always know if your variable is allocated on the stack or heap.
...
If you need to know where your variables are allocated pass the "-m" gc flag to "go build" or "go run" (e.g., go run -gcflags -m app.go).

Source: http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#stack_heap_vars

46
type MyStructType struct{}

func myFunction1() (*MyStructType, error) {
    var chunk *MyStructType = new(MyStructType)
    // ...
    return chunk, nil
}

func myFunction2() (*MyStructType, error) {
    var chunk MyStructType
    // ...
    return &chunk, nil
}

struct типаMyStructType на кучу и верни его адрес. Функции эквивалентны; источник asm компилятора такой же.

--- prog list "myFunction1" ---
0000 (temp.go:9) TEXT    myFunction1+0(SB),$8-12
0001 (temp.go:10) MOVL    $type."".MyStructType+0(SB),(SP)
0002 (temp.go:10) CALL    ,runtime.new+0(SB)
0003 (temp.go:10) MOVL    4(SP),BX
0004 (temp.go:12) MOVL    BX,.noname+0(FP)
0005 (temp.go:12) MOVL    $0,AX
0006 (temp.go:12) LEAL    .noname+4(FP),DI
0007 (temp.go:12) STOSL   ,
0008 (temp.go:12) STOSL   ,
0009 (temp.go:12) RET     ,

--- prog list "myFunction2" ---
0010 (temp.go:15) TEXT    myFunction2+0(SB),$8-12
0011 (temp.go:16) MOVL    $type."".MyStructType+0(SB),(SP)
0012 (temp.go:16) CALL    ,runtime.new+0(SB)
0013 (temp.go:16) MOVL    4(SP),BX
0014 (temp.go:18) MOVL    BX,.noname+0(FP)
0015 (temp.go:18) MOVL    $0,AX
0016 (temp.go:18) LEAL    .noname+4(FP),DI
0017 (temp.go:18) STOSL   ,
0018 (temp.go:18) STOSL   ,
0019 (temp.go:18) RET     ,

Calls

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.

Все функции и возвращаемые параметры передаются по значению. Возвращаемое значение параметра с типом*MyStructType это адрес.

Ах, понял - gcflags.
Спасибо большое! Проголосовано, но я принимаю Соню из-за небольшого количества информации об анализе побега. Joe
peterSo, как вы и @Sonia создаете эту сборку? У вас обоих одинаковое форматирование. Я не могу произвести это независимо от команды / флагов, попробовав objdump, go tool, otool.
132

что слова «составлять» и "куча" не появляется нигде в спецификации языка. Ваш вопрос сформулирован как "... объявлен в стеке" & quot; и "... объявлено в куче" & quot; но обратите внимание, что синтаксис объявления Go ничего не говорит о стеке или куче.

Это технически делает ответ на все ваши вопросы зависимыми от реализации. На самом деле, конечно, есть стек (на каждую процедуру!) И куча, и некоторые вещи идут в стек, а некоторые в кучу. В некоторых случаях компилятор следует строгим правилам (например, & quot;new всегда размещается в куче "), а в других компилятор" избегает анализа " решить, может ли объект жить в стеке или он должен быть размещен в куче.

В вашем примере 2 escape-анализ покажет указатель на escape-структуру, и поэтому компилятор должен будет выделить структуру. Я думаю, что текущая реализация Go в этом случае следует строгому правилу: если адрес берется из какой-либо части структуры, структура попадает в кучу.

На вопрос 3 мы рискуем запутаться в терминологии. Все в Go передается по значению, по ссылке нет передачи. Здесь вы возвращаете значение указателя. Какой смысл указателей? Рассмотрим следующую модификацию вашего примера:

type MyStructType struct{}

func myFunction1() (*MyStructType, error) {
    var chunk *MyStructType = new(MyStructType)
    // ...
    return chunk, nil
}

func myFunction2() (MyStructType, error) {
    var chunk MyStructType
    // ...
    return chunk, nil
}

type bigStruct struct {
    lots [1e6]float64
}

func myFunction3() (bigStruct, error) {
    var chunk bigStruct
    // ...
    return chunk, nil
}

Я изменил myFunction2, чтобы он возвращал структуру, а не адрес структуры. Сравните вывод сборки myFunction1 и myFunction2 сейчас,

--- prog list "myFunction1" ---
0000 (s.go:5) TEXT    myFunction1+0(SB),$16-24
0001 (s.go:6) MOVQ    $type."".MyStructType+0(SB),(SP)
0002 (s.go:6) CALL    ,runtime.new+0(SB)
0003 (s.go:6) MOVQ    8(SP),AX
0004 (s.go:8) MOVQ    AX,.noname+0(FP)
0005 (s.go:8) MOVQ    $0,.noname+8(FP)
0006 (s.go:8) MOVQ    $0,.noname+16(FP)
0007 (s.go:8) RET     ,

--- prog list "myFunction2" ---
0008 (s.go:11) TEXT    myFunction2+0(SB),$0-16
0009 (s.go:12) LEAQ    chunk+0(SP),DI
0010 (s.go:12) MOVQ    $0,AX
0011 (s.go:14) LEAQ    .noname+0(FP),BX
0012 (s.go:14) LEAQ    chunk+0(SP),BX
0013 (s.go:14) MOVQ    $0,.noname+0(FP)
0014 (s.go:14) MOVQ    $0,.noname+8(FP)
0015 (s.go:14) RET     ,

Не беспокойтесь, что вывод myFunction1 здесь отличается от ответа peterSO (отлично). Мы, очевидно, работаем с разными компиляторами. В противном случае убедитесь, что я изменил myFunction2, чтобы он возвращал myStructType, а не * myStructType. Призыв к runtime.new пропал, что в некоторых случаях было бы хорошо. Держись, хотя, здесь myFunction3,

--- prog list "myFunction3" ---
0016 (s.go:21) TEXT    myFunction3+0(SB),$8000000-8000016
0017 (s.go:22) LEAQ    chunk+-8000000(SP),DI
0018 (s.go:22) MOVQ    $0,AX
0019 (s.go:22) MOVQ    $1000000,CX
0020 (s.go:22) REP     ,
0021 (s.go:22) STOSQ   ,
0022 (s.go:24) LEAQ    chunk+-8000000(SP),SI
0023 (s.go:24) LEAQ    .noname+0(FP),DI
0024 (s.go:24) MOVQ    $1000000,CX
0025 (s.go:24) REP     ,
0026 (s.go:24) MOVSQ   ,
0027 (s.go:24) MOVQ    $0,.noname+8000000(FP)
0028 (s.go:24) MOVQ    $0,.noname+8000008(FP)
0029 (s.go:24) RET     ,

До сих пор нет вызова runtime.new, и да, он действительно работает, чтобы возвратить 8 МБ объект по значению. Это работает, но обычно вы этого не хотите. Точка указателя здесь должна была бы избегать проталкивания объектов размером 8 МБ.

Краткое объяснение сборки будет оценено.
Отлично, спасибо. Я действительно не спрашивал «в чем смысл вообще указателей», это было больше похоже на то, «в чем смысл указателей», когда значения ведут себя как указатели », и этот случай становится спорным из-за вашего ответа тем не мение. Joe

Похожие вопросы