Компилятор Go применяет механизм escape analysis (анализ ухода указателей): все значения по умолчанию размещаются на стеке, но если переменная "покидает" область видимости (например, возвращается указатель на локальную переменную), она автоматически размещается в куче (heap).
Это важно для производительности:
Компилятор пытается определить, можно ли разместить объект на стеке, но если он неявно “выживает” пределы функции — кладет в кучу.
Пример с уходом:
func NewPoint() *int { a := 42 return &a // escape to heap! }
Пример без ухода:
func Sum(a, b int) int { c := a + b return c // на стеке }
Для диагностики компилятора используйте go build -gcflags='-m', чтобы увидеть подробности:
go build -gcflags='-m' main.go
"Уйдет ли объект в кучу, если вернуть из функции только его значение — не указатель?"
Часто полагают, что любые возвращаемые значения "уходят в кучу". На самом деле, если возвращается значение (не указатель), переменная может остаться на стеке.
Пример:
func F() int { x := 10 return x // stack allocation, не уходит в heap }
История
При массовом создании структур через фабричную функцию, возвращающую указатели на локальные переменные, резко увеличилось потребление памяти и нагрузка на GC. Оказалось: все объекты уходили в heap из-за возврата указателя на локальную переменную, хотя это можно было избежать, возвращая значение.
История
В микросервисе из-за передачи указателей между горутинами и возврата их из разных функций, из стека “вытекало” множество объектов в heap, что замедляло работу и вызывало частые паузы GC.
История
Разработчик случайно обернул статический массив в срез внутри функции, возвращал указатель на срез — и при нагрузочном тесте начал ловить утечки памяти. Диагностика показала: переменная не "вылазила" за пределы функции, но Go ошибочно решил поместить ее в heap из-за непрямого использования.