Зачем память в программировании делится на стек (stack) и кучу (heap)?

«Зачем память в программировании делится на стек (stack) и кучу (heap)?» — вопрос из категории Управление памятью, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Разделение памяти на стек (stack) и кучу (heap) обусловлено различиями в требованиях к времени жизни данных, скорости доступа и управлению.

Критерий Стек (Stack) Куча (Heap)
Управление Автоматическое (LIFO). Память выделяется/освобождается при входе/выходе из области видимости. Ручное (вручную через malloc/free) или полуавтоматическое (через ARC/Garbage Collector).
Скорость Очень высокая. Выделение — это просто сдвиг указателя стека. Относительно низкая. Требуется поиск свободного блока и синхронизация в многопоточных средах.
Размер Ограничен (обычно 1-8 МБ на поток). Большой, ограничен виртуальной памятью системы.
Время жизни Привязано к области видимости (scope). Динамическое, может превышать время жизни создавшей его функции.
Фрагментация Отсутствует. Возможна.
Типичное содержимое Локальные переменные, аргументы функций, адреса возврата. Динамически создаваемые объекты (экземпляры классов), большие массивы данных.

Пример в Swift:

func example() {
    // `number` и `point` размещаются на СТЕКЕ.
    // Память освободится автоматически при выходе из функции.
    var number: Int = 10 // Значимый тип (value type)
    var point: CGPoint = .init(x: 0, y: 0) // Структура (value type)

    // `person` — ссылка (указатель) на объект в КУЧЕ.
    // Сама ссылка `person` живет на стеке, а объект `Person` — в куче.
    // Память объекта будет освобождена ARC, когда не останется strong-ссылок.
    let person: Person = Person(name: "Alice") // Класс (reference type)
}

Вывод: Разделение позволяет оптимизировать производительность (быстрый стек для временных данных) и обеспечить гибкость (куча для данных с произвольным временем жизни).