Память в Go: как работает escape analysis и куда уходят переменные

Разбираем стек и кучу в Go на примерах: почему одни переменные живут только внутри функции, а другие убегают в кучу.

Разработка

6 мин

Go — язык, который часто называют простым и быстрым. Но под капотом он делает кучу магии, особенно когда речь идёт о работе с памятью. Понимание того, где и как хранятся данные, может помочь писать более эффективный код, избегать лишних аллокаций и лучше понимать поведение программы.

Стек и куча: основы

  • Стек (stack) — память, которая работает по принципу LIFO (last in, first out). Переменные живут до выхода из функции. Работа со стеком быстрая и почти бесплатная.

  • Куча (heap) — область памяти для объектов, чьё время жизни нельзя заранее определить. Управляется сборщиком мусора (GC). Аллокации дороже, а сборка может замедлять программу.

Главное отличие: на стеке память освобождается автоматически при выходе из функции, а в куче за этим следит GC.

Escape analysis: кто сбежал в кучу?

Когда компилятор Go встречает переменную, он решает, где её хранить — в стеке или куче. Этот процесс называется escape analysis.

Если переменная "сбегает" за пределы функции, она отправляется в кучу. Если нет — остаётся в стеке.

Пример 1. Локальная переменная (остаётся в стеке)

func sum(a, b int) int {
    c := a + b
    return c
}

Пример 2. Переменная уходит в кучу

func makePointer() *int {
    x := 42
    return &x
}

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

Проверяем escape analysis

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

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

./main.go:5:6: moved to heap: x

Когда переменные убегают в кучу?

  • Возвращаются из функции по указателю.

  • Сохраняются в замыкании.

  • Передаются в интерфейс (иногда).

  • Размер структуры слишком большой, чтобы поместиться в стек.

Пример 3. Замыкание

func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

Здесь sum хранится в куче, потому что нужно, чтобы она жила дольше функции adder.

Стек vs куча на практике

Характеристика

Стек

Куча

Время жизни

Пока выполняется функция

До сборки GC

Скорость доступа

Очень быстрая

Медленнее

Управление

Автоматическое

Сборщик мусора

Где хранятся

Локальные переменные

Указатели, замыкания, долгоживущие объекты

Как писать эффективный код

  • Избегайте лишних указателей. Чем больше указателей, тем выше шанс уйти в кучу.

  • По возможности используйте значения, а не ссылки.

  • Следите за escape analysis. Иногда Go аллоцирует память в куче, хотя кажется, что это не нужно.

  • Не бойтесь кучи слишком сильно. Оптимизация важна, но преждевременная оптимизация — зло.

Итог

Понимание того, где хранятся данные в Go, позволяет писать более производительный код. Escape analysis — это инструмент, который решает судьбу каждой переменной. А зная его правила, можно избежать неожиданных тормозов из-за лишних аллокаций в куче.

В Кодике есть полноценный курс по Go — от самых основ до продвинутых тем. Мы разбираем синтаксис, работу с памятью, конкурентность и пишем мини-проекты, которые помогут закрепить знания.

А ещё у нас есть активный telegram-канал, где мы обсуждаем крутые идеи, делимся опытом и вместе разбираем задачи — учиться становится не только полезно, но и весело.

Комментарии