Подтвердить что ты не робот

Формат памяти Golang по сравнению с С++/C

В golang, кажется, нет конструкторов, но предлагается выделить объект типа struct с помощью функции, обычно называемой "New" + TypeName, например

func NewRect(x,y, width, height float) *Rect {
     return &Rect(x,y,width, height)
}

Однако я не уверен в макете памяти Go. В C/С++ этот тип кода означает, что вы возвращаете указатель, который указывает на временный объект, потому что переменная выделяется в стеке, а переменная может быть некоторая корзина после возврата функции. В Голанге мне приходится беспокоиться об этом? Потому что кажется, что нет стандартного представления о том, какие данные будут распределены в стеке против того, какие данные будут выделены в куче.

Как и в Java, существует определенная точка зрения, что базовый тип, такой как int, float, будет выделен в стеке, другой объект, выделенный из объекта, будет выделен в куче. В golang, есть конкретный разговор об этом?

4b9b3361

Ответ 1

Составный литерал упоминает:

Взятие адреса составного литерала (§Address operator) генерирует уникальный указатель на экземпляр буквального значения.

Это означает, что указатель, возвращаемый функцией New, будет действительным (выделенным в стеке).
Calls:

В вызове функции значение функции и аргументы оцениваются в обычном порядке.
После их оценки параметры вызова передаются по значению функции, и вызываемая функция начинает выполнение.
Возвращаемые параметры функции передаются по значению обратно вызывающей функции, когда функция возвращает.

Вы можете увидеть больше в этом ответе и этот поток.

Как упоминалось в разделе "Stack vs heap-распределение структур в Go и как они относятся к сборке мусора:

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


сообщение в блоге "Escape Analysis in Go" детализирует, что происходит, упоминание часто задаваемых вопросов:

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

В блоге добавлено:

Код, который выполняет "анализ побега", находится в src/cmd/gc/esc.c.
Понятно, что он пытается определить, выходит ли локальная переменная из текущей области; только в двух случаях, когда это происходит, когда возвращается адрес переменных и когда его адрес присваивается переменной во внешней области.
Если переменная ускользает, она должна быть выделена в куче; в противном случае его можно будет положить в стек.

Интересно, что это относится и к распределениям new(T).
Если они не убегут, они будут распределены в стеке. Вот пример, чтобы прояснить ситуацию:

var intPointerGlobal *int = nil

func Foo() *int {
    anInt0 := 0
    anInt1 := new(int)

    anInt2 := 42
    intPointerGlobal = &anInt2

    anInt3 := 5

    return &anInt3
}

Выше, anInt0 и anInt1 не исчезают, поэтому они выделены в стеке; anInt2 и anInt3, и выделяются в куче.


См. также "Пять вещей, которые делают Go fast ":

В отличие от C, который заставляет вас выбирать, будет ли значение храниться в куче, через malloc или в стеке, объявив его внутри области действия функции, Go реализует оптимизацию, называемую escape-анализом.

Оптимизация Gos всегда включена по умолчанию.
Вы можете увидеть компиляторы для анализа и вложения решений с помощью -gcflags=-m.

Поскольку анализ эвакуации выполняется во время компиляции, а не во время выполнения, распределение стека всегда будет быстрее, чем распределение кучи, независимо от того, насколько эффективен ваш сборщик мусора.