Стек и куча в Go. Когда переменные размещаются в стеке, а когда — в куче?

Ответ

В Go компилятор самостоятельно решает, где разместить переменную, используя механизм escape analysis (анализ "убегания"). Основная идея — если переменная не используется за пределами функции, в которой она была создана, её можно безопасно разместить на стеке.

Стек (Stack)

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

  • Что размещается: Локальные переменные базовых типов (int, bool), структуры и указатели, время жизни которых ограничено одной функцией.
  • Преимущества: Очень быстрое выделение и освобождение памяти (просто сдвиг указателя стека), не создает нагрузку на сборщик мусора (GC).
func onStack() int {
    x := 42 // x не "убегает" и размещается на стеке
    return x
}

Куча (Heap)

Память выделяется в куче, если переменная должна "пережить" вызов функции, в которой была создана. Например, если на неё остается ссылка.

  • Что размещается: Переменные, на которые возвращается указатель; переменные, используемые в замыканиях или горутинах; данные для слайсов, мап, каналов.
  • Особенности: Выделение памяти медленнее, чем на стеке. Память управляется сборщиком мусора (GC).
func onHeap() *int {
    x := 42 // x "убегает" в кучу, так как на неё возвращается указатель
    return &x
}

Проверить, куда компилятор разместил переменную, можно с помощью флага -m:
go build -gcflags="-m" .