Ответ
Компилятор Go выделяет память в куче, когда переменная "убегает" (escapes) из своего первоначального скоупа. Это происходит в следующих основных случаях:
-
Возврат указателя на локальную переменную. Если функция возвращает адрес своей локальной переменной, эта переменная должна продолжать существовать после завершения функции, поэтому она перемещается в кучу.
func createInt() *int { x := 100 // "escapes to heap" return &x }
-
Отправка указателя в горутину или канал. Компилятор не может отследить время жизни переменной, если она используется в другой горутине. Для безопасности он размещает её в куче.
func main() { data := "some data" // "escapes to heap" go func() { fmt.Println(data) // Замыкание использует data }() }
-
Размер переменной неизвестен на этапе компиляции. Данные для слайсов, мап и каналов всегда размещаются в куче, так как их размер может динамически меняться. Сама структура (заголовок слайса, указатель на мапу) может остаться на стеке.
// Резервирование памяти под 1000 элементов происходит в куче s := make([]int, 1000)
-
Переменная слишком большая для стека. Даже если переменная не "убегает", но имеет очень большой размер, компилятор может разместить её в куче, чтобы избежать переполнения стека.
-
Сохранение значения в переменной типа
interface{}
. Когда значение присваивается переменной типаinterface{}
, происходит "упаковка" (boxing). Данные и информация о типе этого значения размещаются в куче.var i interface{} s := "hello world" // "s escapes to heap" i = s