Почему работа с кучей медленнее, чем со стеком?

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

Ответ

Основные причины снижения производительности кучи по сравнению со стеком:

1. Динамическое выделение/освобождение:

  • Стек: Работает по принципу LIFO. Выделение и освобождение — это простое перемещение указателя стека.
  • Куча: Требует поиска подходящего свободного блока памяти (алгоритмы malloc/free), что занимает время. Освобождение может приводить к фрагментации.

2. Локализация данных и кэш:

  • Данные в стеке расположены последовательно, что идеально для кэша процессора.
  • Объекты в куче размещаются в произвольных местах, что приводит к промахам кэша (cache misses).

3. Синхронизация (в многопоточных средах): Глобальная куча часто требует блокировок (locks) для безопасного выделения памяти из разных потоков. Стек каждого потока — локальный.

Пример на Swift:

// Быстро: целочисленная переменная размещается в стеке.
func useStack() {
    let localInt = 42 // Выделение — сдвиг указателя стека.
} // Освобождение — обратный сдвиг указателя.

// Медленнее: массив (значение) хранит свои элементы в куче.
func useHeap() {
    let array = [Int](repeating: 0, count: 1000) // Требует запроса к менеджеру кучи.
} // Требует вызова деструктора и освобождения памяти в куче.