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