Ответ
Это решает компилятор Go с помощью механизма анализа побега (escape analysis).
Основной принцип:
- Если переменная не "сбегает" (escape), то есть используется только в пределах своей функции и её жизненный цикл не превышает время выполнения этой функции, она размещается на стеке. Это очень эффективно.
- Если компилятор обнаруживает, что на переменную могут сослаться после завершения функции, она "сбегает" и размещается в куче (heap), чтобы данные остались доступны. Управление памятью в куче берёт на себя сборщик мусора.
Примеры "побега" переменной в кучу:
- Возврат указателя на локальную переменную из функции.
- Сохранение указателя на переменную в глобальной переменной или в структуре, которая сама находится в куче.
- Отправка указателя в другую горутину через канал.
// x будет на стеке, так как не "сбегает"
func onStack() int {
x := 42
return x
}
// y "сбегает", так как мы возвращаем на неё указатель.
// Поэтому y будет размещена в куче.
func onHeap() *int {
y := 42
return &y
}
Проверить результаты анализа побега можно с помощью флага -m
:
go build -gcflags="-m" ./...