Вопросы по JVM на собеседовании
JVM спрашивают, чтобы понять, доводилось ли вам разбираться, почему сервис умирает по памяти. Теория про поколения и сборщики нужна, но ценнее ответ на вопрос, что вы делали, когда приложение начало тормозить.
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- Сборка мусора (GC)17
- JIT и исполнение16
- Загрузка классов16
- Модель памяти15
- Диагностика и утечки13
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- Загрузка классов в JVM16 вопросов
- Диагностика утечек памяти в Java13 вопросов
- Сборка мусора в JVM17 вопросов
- JIT и исполнение байткода16 вопросов
- Модель памяти JVM15 вопросов
Примеры вопросов с разбором
- Как устроена модель делегирования загрузчиков классов (parent delegation)?A)Загрузчик сначала делегирует загрузку родителю и грузит сам, только если тот не смогB)Загрузчик сам грузит класс и лишь потом сообщает родителю, какой класс был загруженC)Все классы грузит один системный загрузчик, иерархии загрузчиков в Java практически нетD)Дочерний и родительский загрузчики грузят каждый класс параллельно, а затем сверяют результаты
показать ответ и разбор
+A)Загрузчик сначала делегирует загрузку родителю и грузит сам, только если тот не смог// разбор: При запросе класса загрузчик СНАЧАЛА делегирует его родителю (и так вверх до bootstrap), и грузит сам только если ни один родитель класс не нашёл. Иерархия: Bootstrap (ядро JDK) → Platform → Application (classpath). Делегирование гарантирует, что базовые классы (java.lang.String) грузит именно bootstrap — их нельзя подменить своим java.lang.String с classpath (безопасность и уникальность). Идентичность класса = его имя + загрузчик.
- Что из перечисленного — типичная причина утечки памяти в Java?A)Бесконечно растущая static-коллекция/кэш без вытесненияB)Слишком частые вызовы сборщика мусора, из-за которых объекты не успевают освобождаться вовремяC)Использование локальных переменных в методах — они остаются в куче и после выхода из методаD)Создание большого числа короткоживущих объектов внутри цикла — они переполняют старое поколение
показать ответ и разбор
+A)Бесконечно растущая static-коллекция/кэш без вытеснения// разбор: Классические утечки — там, где ссылки НЕ отпускаются, хотя объект уже не нужен: статические коллекции/кэши, растущие без ограничения; забытые слушатели/колбэки/подписки; незачищенные ThreadLocal (особенно в пуле потоков); удержанные загрузчики классов. GC такие объекты не собирает — они достижимы от корней. Лечение: ограниченные кэши (LRU/WeakHashMap), обязательная отписка, очистка ThreadLocal, аудит heap dump. Короткоживущие объекты, наоборот, — забота дешёвого minor GC.
- На каком принципе строится поколенческая (generational) сборка мусора?A)Большинство объектов умирает молодыми — молодое поколение чистят часто и быстроB)Объекты сортируются по размеру: крупные собираются часто, а мелкие — раз в несколько часов работыC)Все объекты живут одинаково долго, поэтому куча делится на равные части и чистится строго по кругуD)Старые объекты собираются первыми, так как именно они чаще всего становятся ненужными приложению
показать ответ и разбор
+A)Большинство объектов умирает молодыми — молодое поколение чистят часто и быстро// разбор: Поколенческая сборка опирается на эмпирику: ПОДАВЛЯЮЩЕЕ большинство объектов живёт очень недолго («умирают молодыми»). Поэтому кучу делят на young generation (Eden + два Survivor) и old generation. Молодое поколение собирают часто и быстро (minor GC), переживших несколько раз объектов «повышают» в старое; старое собирают реже и дороже (major/full GC). Так минимизируется объём работы: не сканировать всю кучу каждый раз.
- Как JVM исполняет байткод?A)Сначала интерпретирует, а горячие участки компилирует в машинный код JIT-омB)Компилирует весь байткод в машинный код заранее при старте, поэтому первая же итерация уже оптимальнаC)Только интерпретирует байткод построчно, никакой компиляции в нативный код в JVM не происходитD)Переводит байткод обратно в исходный.java и запускает его через встроенный интерпретатор языка
показать ответ и разбор
+A)Сначала интерпретирует, а горячие участки компилирует в машинный код JIT-ом// разбор: HotSpot исполняет байткод СМЕШАННО: сначала интерпретирует (быстрый старт), одновременно профилируя исполнение, а «горячие» (часто вызываемые) методы компилирует JIT-ом в оптимизированный машинный код прямо во время работы. Это адаптивная оптимизация: JIT знает реальные пути исполнения и оптимизирует агрессивнее статического компилятора, при необходимости деоптимизируя обратно. Поэтому пиковой скорости приложение достигает не сразу, а после «прогрева».
- Где размещаются объекты и где — локальные примитивы/ссылки в JVM?A)Объекты — в куче (heap); локальные примитивы и ссылки — в стеке потокаB)Всё подряд — и объекты, и локальные переменные — хранится в общей куче, стек в Java не используетсяC)Объекты лежат в стеке каждого потока, а куча отведена только под загруженные классы и их методыD)Объекты и локальные переменные размещаются в metaspace, а куча используется лишь под строки-литералы
показать ответ и разбор
+A)Объекты — в куче (heap); локальные примитивы и ссылки — в стеке потока// разбор: Куча (heap) — общая для всех потоков область, где живут ВСЕ объекты (включая массивы). Стек — свой у каждого потока: в его кадрах лежат локальные примитивы и ССЫЛКИ на объекты (сами объекты — в куче). Метаданные классов — в metaspace (нативная память). Поэтому объект доступен из разных потоков (общая куча), а локальные переменные потоко-локальны. Управляет кучей сборщик мусора; кадр стека освобождается при выходе из метода.
- Какие есть встроенные загрузчики классов в современной JVM?A)Только один загрузчик — системный; остальные существовали лишь в очень ранних версиях JavaB)Bootstrap, Platform и Application (system) — по иерархииC)Compile-loader и Runtime-loader: первый грузит классы при компиляции, второй — при исполненииD)По одному загрузчику на каждый пакет приложения, они создаются автоматически под каждый пакет
показать ответ и разбор
+B)Bootstrap, Platform и Application (system) — по иерархии// разбор: Стандартная иерархия (с Java 9): Bootstrap загружает ядро JDK (java.base); Platform — платформенные модули; Application (он же System) — классы приложения с classpath/module-path. Раньше между Bootstrap и App был Extension ClassLoader — его заменил Platform. Приложения могут добавлять свои URLClassLoader (плагины, серверы приложений). Именно на нестандартных загрузчиках чаще возникают утечки metaspace и конфликты версий классов.
- О чём говорит OutOfMemoryError: Java heap space?A)Закончилось место на жёстком диске, куда JVM сбрасывает объекты из переполненной оперативной памятиB)Куче не хватает места: утечка, малый -Xmx или реально большой объём данныхC)Переполнился стек одного из потоков из-за слишком глубокой рекурсии в коде приложенияD)Metaspace исчерпан загруженными классами, и новые классы загрузить не получится
показать ответ и разбор
+B)Куче не хватает места: утечка, малый -Xmx или реально большой объём данных// разбор: «OutOfMemoryError: Java heap space» — сборщик не смог освободить достаточно места в куче под новые объекты. Причины: утечка (объекты копятся, будучи достижимыми), слишком маленький -Xmx под реальный объём, или объективно большой набор данных/пиковая нагрузка. Диагностика: снять heap dump (-XX:+HeapDumpOnOutOfMemoryError), проанализировать в MAT/VisualVM кто держит память, посмотреть GC-логи. Это ОТДЕЛЬНАЯ ошибка от Metaspace-OOM, StackOverflowError и «unable to create native thread».
- Какой сборщик мусора используется по умолчанию в современной JVM (Java 9+)?A)Serial GC — однопоточный сборщик, оптимальный для серверов с большими кучами и многими ядрамиB)G1 GCC)CMS (Concurrent Mark-Sweep) — он остаётся дефолтным и рекомендуемым выбором в новых версияхD)Виртуальный сборщик, который не освобождает память, а лишь помечает объекты для ручной очистки
показать ответ и разбор
+B)G1 GC// разбор: С Java 9 сборщик по умолчанию — G1 (Garbage-First): регионный, стремится укладываться в целевую паузу, хорошо подходит для средних-больших куч. Serial — для маленьких приложений/одного ядра; Parallel — на максимум throughput; CMS удалён. Для сверхнизких пауз (сотни ГБ, sub-ms) есть ZGC и Shenandoah. Выбор GC — компромисс между длиной пауз и пропускной способностью, задаётся флагами (-XX:+UseZGC и т.п.).
- Что такое tiered compilation (C1 и C2)?A)Разделение кода на два потока: C1 исполняет чётные методы, а C2 — нечётные, ради параллелизмаB)Многоуровневая компиляция: C1 быстро компилит, C2 позже оптимизирует горячее агрессивноC)Компиляция в два прохода на диске: C1 создаёт.class, а C2 превращает его в нативный образ заранееD)Две независимые JVM, где C1 запускает приложение, а C2 дублирует его для отказоустойчивости
показать ответ и разбор
+B)Многоуровневая компиляция: C1 быстро компилит, C2 позже оптимизирует горячее агрессивно// разбор: Tiered compilation комбинирует два JIT-компилятора: C1 (client) компилирует быстро с базовой оптимизацией и собирает профиль, а C2 (server) позже перекомпилирует САМЫЕ горячие методы с агрессивными оптимизациями (инлайнинг, устранение аллокаций и т.д.). Так достигается и быстрый прогрев (C1), и высокая пиковая производительность (C2). Уровни переключаются по счётчикам вызовов/циклов. GraalVM предлагает альтернативный C2 и AOT-компиляцию в нативный образ.
это 9 из 77
Ещё 68 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Что нужно знать про сборку мусора?
Идею поколений, разницу между молодой и старой областями, что вызывает длинные паузы и чем современные сборщики отличаются друг от друга по цели.
Как отвечать про утечку памяти в Java?
Через механизм: объекты остаются достижимыми, поэтому не собираются. Полезно назвать типичные источники, например статические коллекции и незакрытые ресурсы, и рассказать про снятие дампа кучи.