Чем стек отличается от кучи?
- Где хранятся локальные переменные и объекты?
- Что такое StackOverflowError?
- Почему выделение на стеке быстрее, чем в куче?
Стек — память для вызовов функций: локальные переменные и адрес возврата, выделяется и освобождается автоматически при входе и выходе. Куча — память для данных, чьё время жизни не привязано к вызову.
- Стек у каждого потока свой, небольшой (обычно 1–8 МБ), работает как LIFO
- Выделение на стеке — сдвиг указателя, почти бесплатно
- Куча общая для потоков; освобождает программист или сборщик мусора
А ещё Глубокая рекурсия переполняет стек — StackOverflowError, RecursionError
Подробно
Стек вызовов. При вызове функции на стек кладётся кадр (stack frame): аргументы, локальные переменные, адрес возврата. При выходе кадр снимается. Память выделяется и освобождается сдвигом одного указателя, поэтому это очень быстро и не фрагментирует память. Ограничения: размер стека фиксирован при создании потока (в Linux по умолчанию 8 МБ для главного потока, в Java -Xss, обычно 512 КБ – 1 МБ), и данные живут только до выхода из функции.
Куча — область для объектов, которые должны пережить вызов или чей размер неизвестен заранее. Выделение сложнее: аллокатор ищет свободный блок подходящего размера, память фрагментируется. Освобождение — вручную (free, delete в C/C++), через подсчёт ссылок (Python, Swift) или сборщиком мусора (Java, Go, C#).
Что где лежит:
- в C/C++ локальные массивы и структуры — на стеке,
malloc/new— в куче; - в Java на стеке — примитивы и ссылки, сами объекты — в куче (JIT может разместить объект на стеке после escape-анализа, если он не «убегает» из метода);
- в Python все объекты в куче, на стеке интерпретатора — ссылки;
- в Go компилятор сам решает по escape-анализу.
Переполнение стека. Каждый рекурсивный вызов добавляет кадр. Без базового случая или при глубине в десятки тысяч стек кончается: StackOverflowError в Java, RecursionError в Python (там лимит глубины 1000 по умолчанию стоит раньше реального стека). Лечится переписыванием на цикл с явным стеком.
Потоки. Стек — у каждого потока свой, поэтому локальные переменные потокобезопасны сами по себе. Куча общая — объект, на который ссылаются два потока, нужно синхронизировать.
Не путать со структурами данных: «куча» как область памяти и двоичная куча (heap) — разные вещи с одинаковым названием.
Пример
import sys
sys.getrecursionlimit() # 1000
def depth(n):
return 0 if n == 0 else 1 + depth(n - 1)
depth(500) # работает
depth(5000) # RecursionError: maximum recursion depth exceeded
def depth_loop(n): # тот же результат без роста стека
d = 0
while n: n -= 1; d += 1
return dЧто хотят услышать
- Стек — кадры вызовов, LIFO, автоматическое освобождение, свой у потока.
- Куча — объекты с произвольным временем жизни, общая, GC или ручное освобождение.
- Почему стек быстрее и почему он ограничен.
- Переполнение стека при рекурсии и как его избежать.
Частые ошибки
- Путать область памяти «куча» со структурой данных «куча».
- Говорить, что в Java объекты бывают на стеке «если маленькие».
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Стек общий для всех потоков, а куча у каждого потока своя
- ✗Выделение на стеке дороже, чем в куче, поэтому объекты кладут в кучу
- ✓Стек — для вызовов функций, свой у каждого потока; куча — для данных с независимым временем жизни
- ✗Размер стека потока не ограничен, поэтому глубокая рекурсия безопасна
Что спросят дальше
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.