Что определяет, будет ли переменная в Go размещена в стеке или в куче?

Ответ

Это решает компилятор Go с помощью механизма анализа побега (escape analysis).

Основной принцип:

  • Если переменная не "сбегает" (escape), то есть используется только в пределах своей функции и её жизненный цикл не превышает время выполнения этой функции, она размещается на стеке. Это очень эффективно.
  • Если компилятор обнаруживает, что на переменную могут сослаться после завершения функции, она "сбегает" и размещается в куче (heap), чтобы данные остались доступны. Управление памятью в куче берёт на себя сборщик мусора.

Примеры "побега" переменной в кучу:

  1. Возврат указателя на локальную переменную из функции.
  2. Сохранение указателя на переменную в глобальной переменной или в структуре, которая сама находится в куче.
  3. Отправка указателя в другую горутину через канал.
// x будет на стеке, так как не "сбегает"
func onStack() int {
    x := 42
    return x
}

// y "сбегает", так как мы возвращаем на неё указатель.
// Поэтому y будет размещена в куче.
func onHeap() *int {
    y := 42
    return &y
}

Проверить результаты анализа побега можно с помощью флага -m:

go build -gcflags="-m" ./...