Как устроены стек и куча, в чем их разница и для чего они нужны
Что проверяют
Понимаете ли вы разницу во времени жизни и стоимости, и знаете ли, что где размещается в Swift.
Как отвечать
Стек это область для вызовов функций. Работает по принципу «последним пришёл, первым ушёл»: при входе в функцию кадр кладётся, при выходе снимается. Выделение это сдвиг указателя, то есть почти бесплатно. Размер ограничен (обычно около мегабайта на поток, у главного больше), поэтому глубокая рекурсия даёт переполнение стека. У каждого потока свой стек.
Куча это область для объектов, чьё время жизни не совпадает с вызовом функции. Выделение дороже: нужен поиск свободного блока и учёт. Освобождение в Swift делает ARC по счётчику ссылок. Куча общая для всех потоков, поэтому доступ к ней требует синхронизации.
Что где в Swift:
- локальные переменные значимых типов (
struct,enum, кортежи) на стеке; - экземпляры классов в куче, на стеке лежит только ссылка;
- структура, ставшая полем класса или элементом массива, живёт в куче вместе с владельцем. Поэтому «структуры всегда на стеке» это упрощение;
- замыкания, захватывающие контекст, размещают его в куче;
- буферы коллекций в куче, отсюда copy-on-write.
Практический вывод: значимые типы дешевле не потому, что «на стеке», а потому что не требуют подсчёта ссылок. Именно атомарные операции со счётчиком дают заметную часть накладных расходов в горячем коде.
Где спотыкаются
Говорят «структуры на стеке, классы в куче» без оговорок.
Считают, что размер стека можно не учитывать.
Не связывают кучу с подсчётом ссылок и его стоимостью.
Не знают, что у каждого потока свой стек, а куча общая.