Что такое escape-анализ в Go и для чего он нужен?

Ответ

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

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

Компилятор анализирует, может ли переменная "сбежать" (escape) из области видимости функции. Если на переменную останутся ссылки после завершения функции, она должна быть размещена в куче. В противном случае она безопасно размещается в стеке.

Когда происходит "побег" в кучу?

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

Пример:

// x "сбегает" в кучу, так как на него будет ссылка вне функции foo
func foo() *int {
    x := 42
    return &x
}

// y остается в стеке, так как возвращается копия значения
func bar() int {
    y := 10
    return y
}

Как это проверить?
Чтобы увидеть решения компилятора, можно использовать флаг -gcflags="-m" при сборке:
go build -gcflags="-m" main.go

Это покажет, какие переменные были перемещены в кучу (escapes to heap).