Ответ
В Go решение о том, где размещать переменную — в стеке (stack) или в куче (heap) — принимает компилятор на основе анализа побега (escape analysis).
По умолчанию переменные размещаются в стеке, так как это очень быстро (выделение и освобождение памяти — это просто сдвиг указателя стека). Однако, если компилятор определяет, что на переменную будут ссылаться после завершения функции, в которой она была создана, она "сбегает" в кучу. Память в куче управляется сборщиком мусора (GC).
Основные причины "побега" в кучу:
Возврат указателя на локальную переменную: Если функция возвращает указатель на переменную, созданную внутри неё, эта переменная должна пережить вызов функции, поэтому она размещается в куче.
Размер данных неизвестен на этапе компиляции: Например, при создании среза
make([]int, n), гдеn— это переменная, а не константа. Компилятор не может заранее определить размер на стеке.Переменная используется в другой горутине или в замыкании, которое переживает функцию: Так как горутины выполняются независимо, нет гарантии, что стек создавшей их функции будет существовать в момент доступа к переменной.
Сохранение указателя на переменную в срезе, который сам может "сбежать".
Пример:
// user "сбегает" в кучу, так как на него возвращается указатель
func createUser(name string) *User {
u := User{Name: name}
return &u
}
// x НЕ сбегает в кучу, так как используется только внутри функции
func processValue() {
x := 42
fmt.Println(x)
}Как проверить?
Узнать, какие переменные сбегают в кучу, можно с помощью флага компилятора -gcflags="-m":
# Команда покажет результаты анализа побега
go build -gcflags="-m" ./...