Ответ
В Go, в отличие от языков вроде C++, программист не решает напрямую, где будет размещена переменная. Это решение принимает компилятор на основе анализа побега (escape analysis).
Переменная, объявленная в функции, может быть размещена либо в стеке (stack), либо в куче (heap).
-
Стек (Stack)
- Это местоположение по умолчанию для локальных переменных. Память в стеке выделяется и освобождается очень быстро (при входе и выходе из функции).
- Переменная остается в стеке, если компилятор доказывает, что указатель на эту переменную не "сбегает" из функции, то есть не используется после того, как функция завершит свою работу.
func main() { x := 42 // 'x' скорее всего будет в стеке fmt.Println(x) } // Память под 'x' освобождается здесь
-
Куча (Heap)
- Если компилятор обнаруживает, что на переменную будут ссылаться после завершения функции, он размещает её в куче. Память в куче управляется сборщиком мусора (Garbage Collector).
- Это называется "побегом" переменной (variable escapes to the heap).
Примеры, когда переменная "сбегает" в кучу:
-
Возврат указателя на локальную переменную:
func createInt() *int { x := 10 // 'x' "сбежит" в кучу, т.к. указатель на неё возвращается return &x }
-
Переменная используется в замыкании (closure), которое переживает функцию:
func counter() func() int { n := 0 // 'n' "сбежит" в кучу, т.к. используется в возвращаемой функции return func() int { n++ return n } }
Вывод: Место хранения (стек
или куча
) определяется не синтаксисом (var
, :=
, new
), а тем, как переменная используется в коде. Компилятор Go автоматически оптимизирует размещение для баланса между производительностью (стек) и корректностью управления памятью (куча).