Память JVM и сборка мусора на собеседовании
Спрашивают не алгоритм сборщика, а границы областей памяти. Разбираем, что лежит в куче, что в стеке и откуда берётся OutOfMemoryError: Metaspace.
Схемы памяти JVM в разных статьях выглядят по-разному, и это сбивает сильнее, чем сама тема: где-то нарисованы Eden и два Survivor, где-то PermGen, где-то метапространство. Понять, какая схема верна, по картинкам невозможно — потому что верны почти все, просто они про разные вещи. Ниже — граница, которая всё расставляет по местам, и вопросы, которые из неё выводятся.
Всё, что сказано о поведении, относится к Java SE 21 и виртуальной машине HotSpot там, где это оговорено отдельно.
Что на поток, а что общее
Спецификация виртуальной машины делит области памяти по одному признаку, и он объясняет почти всё остальное: одни области принадлежат потоку, другие — общие для всех потоков.
На поток:
- pc-регистр — у каждого потока свой; он хранит адрес выполняемой инструкции;
- стек виртуальной машины — приватный для потока и создаётся вместе с ним; хранит фреймы, а в них — локальные переменные и промежуточные результаты;
- стек нативных методов, если реализация его поддерживает, — обычно тоже создаётся на каждый поток.
Общие для всех потоков:
- куча — создаётся при старте машины; из неё выделяется память для всех экземпляров классов и массивов, и её память освобождает сборщик мусора. Спецификация формулирует это жёстко: объекты никогда не освобождаются явно;
- метод-область — тоже создаётся при старте и хранит структуры уровня класса: пул констант времени выполнения, данные полей и методов, код методов и конструкторов.
Из этой границы сразу выводится ответ на вопрос о том, где живут объект и ссылка на него. Ссылка — локальная переменная — лежит во фрейме на стеке потока; сам объект — в общей куче. Поэтому два потока не видят локальных переменных друг друга, но прекрасно видят один и тот же объект, если оба получили на него ссылку.
Почему OutOfMemoryError бывает разный
OutOfMemoryError воспринимается как одна ошибка с одним лечением — добавить памяти. Спецификация показывает другое: она предписывает эту ошибку в четырёх разных местах, и причины у них разные.
- Куча. Если вычислению нужно больше кучи, чем может предоставить система автоматического управления памятью.
- Стек виртуальной машины. Если стек можно расширять динамически, но памяти на расширение нет — или если её не хватает, чтобы создать начальный стек для нового потока.
- Метод-область. Если память в ней не может быть выделена под запрос.
- Стек нативных методов. Те же два условия, что у стека виртуальной машины.
Отсюда следует вывод, ради которого стоит держать этот список в голове: увеличение кучи лечит ровно один случай из четырёх. Стеки потоков и метод-область — отдельные области, память под них берётся не из кучи, и её размер на них не влияет.
Отдельно стоит различать StackOverflowError и OutOfMemoryError на стеке: имя ошибки разное, область одна и та же. Первая означает, что потоку нужен стек больше разрешённого: рекурсия ушла слишком глубоко. Вторая — что памяти не хватает, чтобы стек создать или расширить. Первая — про глубину вычисления, вторая — про наличие памяти в системе.
Почему в разных статьях разные схемы памяти
Здесь и находится источник путаницы, с которой начинается тема. Спецификация задаёт, какие области существуют, и почти не задаёт, как они устроены внутри. Про кучу сказано, что она общая, создаётся при старте, из неё выделяются объекты и массивы, а освобождает её сборщик. Про то, что она разделена на поколения, не сказано ничего.
Значит, всё привычное устройство — молодое поколение, Eden, области выживших, старое поколение, продвижение объектов между ними — это свойство конкретной реализации, а не общее правило платформы. То же относится к метапространству: в спецификации такого термина нет, там метод-область, а метапространство — то, как её реализует HotSpot.
Отсюда практическое следствие для ответа. Различие стоит проговаривать вслух: не «в куче есть Eden и два Survivor», а «спецификация делит память на области и не описывает устройство кучи; в HotSpot куча поколенческая — Eden, области выживших, старое поколение». Второй ответ ровно на одну фразу длиннее и заметно точнее.
Из того же различения объясняется и путаница с PermGen: это была реализация метод-области в старых версиях HotSpot, которую заменило метапространство. Менялась не спецификация — менялась реализация, а вместе с ней и схемы в статьях разных лет.
Что можно сказать про сборщики точно
Перечисление сборщиков — самая доступная часть темы и потому наименее содержательная: список есть в любом справочнике. Полезнее знать несколько вещей, которые держатся не на памяти, а на источнике.
Что гарантирует платформа. Ровно одно: память кучи освобождается автоматически, и объекты никогда не освобождаются явно. Всё остальное — как именно, когда и с какими паузами — вопрос реализации и настройки.
Что сказано про выбор по умолчанию. Руководство по настройке сборки мусора формулирует это через форму приложения: на серверных машинах — большой объём памяти, два и более процессора — по умолчанию выбирается сборщик G1. Для небольших приложений, которым хватает кучи примерно до ста мегабайт, руководство называет достаточным последовательный сборщик, если специальное поведение других не требуется.
Формулировка «по умолчанию используется G1» без оговорки про класс машины — неточность, и лучше воспроизводить условие: выбор зависит от того, на чём приложение запущено.
Чего не стоит утверждать. Сравнение сборщиков по паузам и пропускной способности — то место, где легче всего сказать лишнее. Разумнее назвать сам факт размена: сборщик решает задачу компромиссом между временем работы приложения, длительностью пауз и расходом памяти, и разные сборщики выбирают разные точки этого компромисса. Конкретные числа зависят от версии, размера кучи и профиля нагрузки, и называть их по памяти не стоит.
Стоит быть готовым к тому, что тема не заканчивается на себе. В опубликованном разборе одного собеседования вопросы про виды сборщиков и типы ссылок идут подряд, и сразу за ними — блок про конкурентные классы и взаимные блокировки. Разбор про многопоточность — соседний.

Backend-разработчик Java
Учебный маршрут backend-разработчика на Java: язык и JVM, коллекции и многопоточность, Spring, реляционные данные и JPA, Kafka, безопасность, продакшн-инженерия и архитектура.

Backend Developer Java
Глубокое понимание Java и JVM для backend-разработки. Курс разбирает не синтаксис, а семантику языка и поведение платформы: коллекции и их стоимость, Stream API, устройство JVM — память, сборку мусора, JIT, — модель памяти и многопоточность, внутренности Spring (контейнер, прокси, транзакции), JPA/Hibernate, Kafka и production-практики: тестирование, наблюдаемость, контейнеры, устойчивость. Цель — научиться видеть стоимость абстракций и поведение кода под нагрузкой, а не просто заставлять его работать.
Готовьтесь не вслепую
Возьмите маршрут по своей роли и отрабатывайте ответы на вопросы с AI-разбором.