Я читал, что язык Голанг разумно управляет памятью. Используя escape-анализ, go может не выделять память при вызове new, и наоборот. Может ли golang выделить память с таким обозначением var bob * Person = & Person {2, 3}. Или всегда указатель будет указывать на стек

0
Nakem1 9 Июн 2021 в 03:02

2 ответа

Лучший ответ

Указатель может уйти в кучу, а может и нет, в зависимости от вашего варианта использования. Компилятор довольно шустрый. Например. дано:

type Person struct {
    b, c int
}


func foo(b, c int) int {
    bob := &Person{b, c}
    return bob.b
}

Функция foo будет скомпилирована в:

    TEXT    "".foo(SB)
    MOVQ    "".b+8(SP), AX
    MOVQ    AX, "".~r2+24(SP)
    RET

Здесь все находится в стеке, потому что, хотя bob является указателем, он не выходит из области видимости этой функции.

Однако если рассматривать небольшую (пусть и искусственную) модификацию:

var globalBob *Person

func foo(b, c int) int {
    bob := &Person{b, c}
    globalBob = bob
    return bob.b
}

Затем bob экранируется, и foo будет скомпилирован в:

    TEXT    "".foo(SB), ABIInternal, $24-24
    MOVQ    (TLS), CX
    CMPQ    SP, 16(CX)
    PCDATA  $0, $-2
    JLS     foo_pc115
    PCDATA  $0, $-1
    SUBQ    $24, SP
    MOVQ    BP, 16(SP)
    LEAQ    16(SP), BP
    LEAQ    type."".Person(SB), AX
    MOVQ    AX, (SP)
    PCDATA  $1, $0
    CALL    runtime.newobject(SB)
    MOVQ    8(SP), AX
    MOVQ    "".b+32(SP), CX
    MOVQ    CX, (AX)
    MOVQ    "".c+40(SP), CX
    MOVQ    CX, 8(AX)
    PCDATA  $0, $-2
    CMPL    runtime.writeBarrier(SB), $0
    JNE     foo_pc101
    MOVQ    AX, "".globalBob(SB)
 foo_pc83:
    PCDATA  $0, $-1
    MOVQ    (AX), AX
    MOVQ    AX, "".~r2+48(SP)
    MOVQ    16(SP), BP
    ADDQ    $24, SP
    RET

Как видите, вызывает newobject.


Эти листинги дизассемблирования были созданы https://godbolt.org/ и предназначены для версии 1.16 на amd64.

3
Eli Bendersky 9 Июн 2021 в 00:21

Распределение памяти в стеке или "утечка" в кучу полностью зависит от того, как вы используете память, а не от того, как вы объявляете переменную.

Если вы вернете указатель на переменную, выделенную стеком, например, в C, значение вашего указателя будет недействительным к тому времени, когда вы попытаетесь его использовать. Это невозможно в Go, потому что вы не можете явно указать Go, где разместить переменную. Он очень хорошо выбирает правильное место, и если он видит, что ссылки на большой двоичный объект памяти могут находиться за пределами кадра стека, он гарантирует, что вместо этого выделение происходит в куче.

Может ли golang выделить память с таким обозначением

var bob * Person = & Person {2, 3}

Или всегда указатель будет указывать на стек

Эта строка кода нельзя сказать, что она «всегда» указывает на стек, но иногда может, так что да, она может выделять память (в куче).

Опять же, дело не в этой строчке кода, а в том, что будет после нее. Если возвращается значение bob (адрес объекта Person), то его нельзя выделить в стеке, потому что возвращенный адрес будет указывать на освобожденную память.

1
meager 9 Июн 2021 в 00:25