В чем разница между стеком и кучей?

Ответ

Стек и куча — это области памяти, используемые для хранения данных, но с разными характеристиками:

  • Стек:

    • Выделяется автоматически (например, для локальных переменных).
    • Быстрый доступ, но ограниченный размер.
    • Освобождается при выходе из функции (LIFO - Last In, First Out).
    • В Go стек используется для локальных переменных и значений, которые, согласно анализу побега (escape analysis), не 'убегают' (escape) за пределы текущей функции. Это могут быть примитивы, структуры, массивы и даже заголовки слайсов/карт, если они не используются после завершения функции.
  • Куча:

    • Динамическое выделение памяти (например, через new или make).
    • Медленнее, но размер ограничен только ОЗУ.
    • Требует сборщика мусора (GC) для освобождения.
    • В Go куча хранит значения, которые 'убегают' (escape) за пределы области видимости их создания (например, возвращаются из функции, передаются по ссылке в другую горутину, или их размер неизвестен на этапе компиляции). Эти объекты требуют сборщика мусора для освобождения.
func main() {
    a := 42          // int, вероятно, в стеке (если не 'убегает')
    b := new(int)    // int в куче (возвращает указатель, значение 'убегает')
    *b = 42

    // Пример, где переменная может 'убежать' в кучу:
    ptr := createAndReturnPointer() // значение, на которое указывает ptr, будет в куче
    _ = ptr
}

func createAndReturnPointer() *int {
    x := 100 // x будет в куче, так как его адрес возвращается
    return &x
}

В Go решение о размещении данных на стеке или в куче принимается компилятором автоматически на основе анализа побега (escape analysis), что избавляет разработчика от ручного управления памятью.

Ответ 18+ 🔞

Да ты посмотри, какие тут у нас, блядь, два царства-государства в памяти завелись! Стек да куча, сука. Один — как аккуратный шкафчик в прихожей, другой — как весь твой гараж, куда уже и зайти-то страшно.

Стек, этот, блядь, шустрый малый:

  • Сам, сука, место себе находит, как только ты в функцию зашёл. Переменные локальные туда сразу — хоп!
  • Быстрый он, овердохуища, но места, блядь, как у мышиной норки — кот наплакал.
  • Как из функции вышел — всё, пиздец, прибрал за собой автоматом. Кто последний зашёл — тот первый вышел, принцип такой, блядь, стопки тарелок.
  • В Go туда всякое барахло летит, которое компилятор посмотрит и скажет: "А этот, ёпта, никуда не денется, тут и сгинет". Примитивы, структуры, массивы — всё может там оказаться, если не убежит.

А куча — это уже, сука, полный раздолбай:

  • Память тут динамически, блядь, выпрашиваешь (new, make).
  • Работает, как черепаха в сиропе, зато места — хоть завались, вся оперативка твоя.
  • А убираться за собой, блядь, не хочет! Тут сборщик мусора (GC) нужен, как дворник с метлой, чтобы не захлебнуться в хламе.
  • В Go сюда летит всё, что "убегает". Возвращаешь из функции указатель? Убежал, сука, в кучу! Передал ссылку в другую горутину? Свалил в кучу! Размер заранее не ясен? Да похуй, отправляй в кучу! И жди потом, когда дворник-сборщик придёт.
func main() {
    a := 42          // Циферка, скорее всего, в стеке приткнётся. Сидит себе, никого не трогает.
    b := new(int)    // А вот это уже указатель, ёпта. Сам `b` может в стеке, а вот число 42, на которое он тычет — уже в куче! Оно убежало.
    *b = 42

    // Или вот, смотри, хитрая жопа:
    ptr := createAndReturnPointer() // Функция-то указатель вернула! Значит, то, что внутри неё создалось — оно в куче, блядь, навеки поселилось.
    _ = ptr
}

func createAndReturnPointer() *int {
    x := 100 // Этому парню хана! Адрес его возвращают наружу — всё, пиши пропало, убежал в кучу.
    return &x
}

И вся красота в том, что в Go тебе, как лоху, об этом думать не надо! Компилятор, блядь, сам всё решает — этот умный анализ побега (escape analysis) проведёт. Посмотрит на переменную и молвит: "Ты, дружок, останешься" или "А ты, сука, убегаешь, марш в кучу!". Во какие распиздяйства умные бывают, в рот меня чих-пых!