Ответ
В Go компилятор сам решает, где размещать переменные, с помощью анализа逃逸 (escape analysis). Основной принцип: если переменная может быть использована после завершения функции, она "сбегает" в кучу. В противном случае она остается в стеке.
Стек (Stack)
Это область памяти, работающая по принципу LIFO (Last-In, First-Out). Память выделяется и освобождается очень быстро — простым сдвигом указателя стека.
Что обычно хранится в стеке:
- Локальные переменные простых типов (
int
,bool
, структуры без указателей), чей жизненный цикл ограничен вызовом функции. - Аргументы, передаваемые в функцию.
- Адреса возврата из функций.
Преимущества:
- Скорость: Очень быстрое выделение и освобождение памяти.
- Автоматическое управление: Память автоматически очищается при выходе из функции, не нагружая сборщик мусора (GC).
Куча (Heap)
Это область памяти для динамического выделения. Объекты в куче могут существовать и после того, как создавшая их функция завершилась.
Что хранится в куче (куда "сбегают" переменные):
- Переменные, на которые есть ссылки за пределами функции (например, если функция возвращает указатель на локальную переменную).
- Данные, захваченные замыканиями (closures), которые переживают функцию.
- Слайсы или мапы, которые растут и требуют переаллокации памяти.
- Данные, отправляемые в каналы (так как их может получить другая горутина в любое время).
Особенности:
- Управление: Память в куче управляется сборщиком мусора (GC), что добавляет накладные расходы.
- Скорость: Выделение памяти медленнее, чем в стеке.
Пример Escape Analysis
// b не сбегает и будет размещена в стеке
func createOnStack() int {
b := 10
return b
}
// p сбегает в кучу, так как на неё будет ссылка
// вне функции
func createOnHeap() *int {
p := new(int)
*p = 42
return p // p "сбегает" в кучу
}
Чтобы проверить, сбегает ли переменная, можно использовать флаг компилятора: go build -gcflags='-m' .