← Назад к вопросам

Что аллоцируется на стеке?

2.0 Middle🔥 181 комментариев
#Основы Go#Производительность и оптимизация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что аллоцируется на стеке в Go?

В Go распределение памяти между стеком (stack) и кучей (heap) определяется не синтаксисом, а эскейп-анализом (escape analysis), который компилятор выполняет во время компиляции. Стек — это область памяти с быстрым доступом, используемая для хранения локальных переменных функций, которая очищается автоматически при выходе из функции. Вот что обычно аллоцируется на стеке:

1. Локальные переменные, не "сбегающие" из функции

Переменные, которые не передаются за пределы функции (например, не возвращаются, не сохраняются в глобальных переменных, не передаются в каналы или не захватываются замыканиями), обычно остаются на стеке.

func calculate() int {
    x := 10          // Аллоцируется на стеке (локальная переменная)
    y := 20
    return x + y
}

2. Аргументы функций

Параметры, передаваемые в функцию, обычно размещаются на стеке, если они не требуют сохранения за пределами вызова.

func add(a, b int) int { // a и b аллоцируются на стеке
    return a + b
}

3. Значения, возвращаемые по значению

Когда функция возвращает значение по значению (не по ссылке), оно обычно размещается на стеке, если размер типа известен и невелик.

type Point struct {
    X, Y int
}

func createPoint() Point {
    p := Point{X: 1, Y: 2} // Скорее всего на стеке
    return p
}

4. Массивы и структуры фиксированного размера

Массивы и структуры с небольшим фиксированным размером, используемые локально, часто остаются на стеке.

func process() {
    var arr [100]int      // Массив фиксированного размера, вероятно на стеке
    for i := range arr {
        arr[i] = i
    }
}

Ключевые исключения: когда аллокация происходит в куче

Эскейп-анализ решает, что должно "сбежать" в кучу. Вот типичные случаи:

  1. Переменные, переживающие функцию: Если на переменную сохраняется ссылка за пределами функции (например, возвращается указатель).

    func escape() *int {
        x := 42 // Аллоцируется в куче, так как ссылка на x возвращается
        return &x
    }
    
  2. Переменные, захваченные замыканиями: Если локальная переменная используется в анонимной функции (closure), она может переместиться в кучу.

    func closure() func() int {
        y := 100 // Может сбежать в кучу из-за захвата замыканием
        return func() int {
            return y
        }
    }
    
  3. Динамически изменяемые данные: Срезы (slices), карты (maps), каналы (channels) и строки (strings) часто используют внутренние указатели на данные в куче, даже если их заголовки могут быть на стеке.

    func sliceExample() []int {
        s := make([]int, 10) // Заголовок slice может быть на стеке, но данные — в куче
        return s
    }
    
  4. Большие объекты: Крупные структуры или массивы могут аллоцироваться в куче, чтобы избежать переполнения стека.

Как проверить эскейп-анализ?

Используйте флаг -gcflags="-m" при компиляции, чтобы увидеть решения компилятора:

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

Пример вывода:

./main.go:10:6: moved to heap: x
./main.go:15:2: y escapes to heap

Практическое значение

  • Производительность: Аллокация на стеке быстрее и не требует сборщика мусора (GC).
  • Управление памятью: Стек очищается автоматически при выходе из функции, тогда как память в куче освобождается GC.
  • Оптимизация: Понимание эскейп-анализа помогает писать эффективный код, минимизирующий аллокации в куче.

В Go разработчик редко управляет этим явно, но знание принципов позволяет избегать неожиданных аллокаций и оптимизировать критический по производительности код.