Всем привет!
Я работаю Senior Java Developer в одном из банков, и за последние годы мне пришлось пройти не одно собеседование, услышать десятки каверзных вопросов и потратить уйму времени на подготовку. И каждый раз убеждаюсь в одном: тема JVM, память и GC — одна из самых недооценённых и одновременно самых частых на Java-собесах. Многие знают об этом “что-то где-то слышал”, но как только разговор заходит про Heap, Metaspace, JIT, Safepoint или разные типы GC — начинаются проблемы.
Поэтому эта статья — вторая часть моей шпаргалки по подготовке к Java-собеседованиям. Здесь я собрал всё самое важное о работе JVM и управлении памятью простым, человеческим языком, без боли и академической зауми. Разберём архитектуру JVM, устройство памяти, JIT, GC, типы ссылок и самые частые вопросы, которые любят спрашивать на интервью.
В профиле уже есть первая часть для подготовки - Многопоточность без боли
Архитектура JVM — кратко и по делу
Class Loader Subsystem
JVM загружает классы с помощью системы загрузчиков (Class Loaders), которая реализует модель делегирования:
Bootstrap ClassLoader – загружает стандартные классы из JDK (
rt.jarили модули JDK).Extension ClassLoader – загружает библиотеки из
extдиректории.Application ClassLoader – загружает классы из classpath.
Пользователь может создать Custom ClassLoader для динамической загрузки (например, в OSGi, плагинах).
Runtime Data Areas
Heap – область, где живут все объекты.
Method Area (Metaspace) – содержит метаинформацию классов, константный пул.
JVM Stack – создается на поток, содержит фреймы вызовов методов.
Program Counter Register (PC Register) — это маленький указатель, который хранит номер следующей команды, которую должен выполнить поток.
Native Method Stack – стек для вызова нативных методов.
Execution Engine
Interpreter – пошаговая интерпретация bytecode.
JIT Compiler (Just-In-Time) – компилирует часто используемые методы в машинный код.
GC Interface – Execution Engine тесно связана со сборщиком мусора: он должен знать, когда объект больше не используется, и правильно освобождать память.
Delegation Model
Delegation Model (модель делегирования ClassLoader-ов) — это механизм, с помощью которого JVM загружает классы в строгом порядке сверху вниз, чтобы избежать конфликтов и дублирования.
Когда ClassLoader хочет загрузить класс, он сначала делегирует эту задачу своему родителю, и только если родитель не нашёл класс, он загружает его сам.
Управление памятью JVM
В JVM память делится на несколько областей, и у каждой своя роль
-
Heap (куча)
Здесь хранятся экземпляры объектов.
Для каждого объекта выделяется место под его поля (данные).
Ссылки на объекты тоже создаются, но обычно эти ссылки хранятся в стеке или в полях других объектов на куче.
-
Stack (стек)
Здесь хранятся локальные переменные метода, включая ссылки на объекты в куче.
Когда метод завершает выполнение, его фрейм из стека удаляется.
-
Metaspace (метаспейс)
Здесь хранятся классы и их структура: методы, константы, информация о полях.
Не хранит сами экземпляры объектов
Heap
-
Young Generation:
o Eden – новые объекты.
o Survivor Spaces (S0/S1) – промежуточные зоны.
Old Generation – объекты, пережившие несколько сборок в Young Gen.
Stack
Каждый поток имеет свой собственный стек, состоящий из фреймов вызова методов. В каждом фрейме хранятся:
локальные переменные метода,
операндный стек для промежуточных вычислений,
ссылка на constant pool класса, чтобы использовать константы и методы этого класса.
Metaspace
Что хранится: информация о классах — их метаданные, методы, константы, аннотации.
Отличие от PermGen: в Java 8+ PermGen убрали, а Metaspace хранится в нативной памяти, а не в heap. Это позволяет JVM динамически расширять пространство для метаданных без жёсткого лимита (если не задан
-XX:MaxMetaspaceSize).Рост и лимиты: растёт по мере загрузки новых классов; можно ограничить через JVM параметры (
MaxMetaspaceSize,MetaspaceSize).
Off-heap Memory
Это память, выделяемая вне стандартного heap JVM, напрямую в нативной памяти.
-
Примеры:
ByteBuffer.allocateDirect()— прямой буфер для работы с нативной памятью, минуя GC.Пулы нативных ресурсов, кэширование больших данных.
-
Опасности:
JVM не отслеживает эту память, поэтому утечки возможны.
Неправильное освобождение памяти (
DirectByteBuffer) может привести к OutOfMemoryError, даже если heap пустой.
Constant pool
Constant Pool — это специальное место в метаданных класса (часть Method Area в JVM), где хранятся константы, литералы и ссылки на методы/классы.
То есть constant pool — это как шкаф с готовыми вещами, а stack — это стол, на котором ты их раскладываешь для работы.
Garbage collector (CG)
GC - механизм JVM, который автоматически освобождает память, занятую объектами, на которые больше нет ссылок.
В современных JVM один Garbage Collector может использовать разные алгоритмы для разных областей памяти:
Copying (копирующий) — применяется в Young Generation. Он быстро копирует живые объекты в новый участок памяти, а все остальное считается мусором.
Mark-Sweep-Compact (отметь-удали-компактизируй) — чаще применяется в Old Generation. Сначала помечаются живые объекты (Mark), затем удаляются мусорные (Sweep), и оставшиеся уплотняются (Compact), чтобы избежать фрагментации.
Виды GC
Serial GC
Использует один поток для всех фаз GC.
Подходит для однопоточных приложений и небольших heap.
Алгоритм: "copying" (в Young Gen) и "mark-sweep-compact" (в Old Gen).
Параметр:
-XX:+UseSerialGC
Parallel GC (Throughput Collector)
Использует несколько потоков для работы в Young и Old Gen.
Цель — максимальная пропускная способность, а не минимизация пауз.
Подходит для серверных приложений без строгих требований к задержкам.
Параметр:
-XX:+UseParallelGC
CMS (Concurrent Mark Sweep) [устарел]
Работает параллельно с приложением (concurrent), уменьшая stop-the-world паузы.
Этапы: initial mark, concurrent mark, remark, sweep.
Не компактизирует память (может привести к фрагментации).
Устарел начиная с Java 9 и удалён в Java 14
Параметр:
-XX:+UseConcMarkSweepGC
G1 GC (Garbage First)
Делит heap на множество регионов.
Каждый регион может быть частью Young или Old Generation.
Этапы GC включают: Initial Mark, Concurrent Mark, Remark, Cleanup, Copy.
Работает по принципу "сборка сначала самых мусорных регионов" (Garbage First).
Использует предсказуемые паузы и старается не превышать
MaxGCPauseMillisПоддерживает инкрементальную, concurrent и компактизирующую сборку Old Gen.
G1 ведёт статистику "полезности" регионов и выбирает наиболее эффективные для сборки.
Параметр:
-XX:+UseG1GCПо умолчанию используется с Java 9+
ZGC (Z Garbage Collector)
Поддерживает heap до терабайт.
Работает с паузами менее 10 мс, независимо от размера heap.
Полностью concurrent (почти все фазы выполняются параллельно с приложением).
Подходит для latency-чувствительных систем.
Параметр:
-XX:+UseZGC
Shenandoah
Похож на ZGC, с акцентом на короткие паузы.
Использует concurrent compacting.
Поддерживается OpenJDK.
Параметр:
-XX:+UseShenandoahGC
Какой GC по умолчанию в Java
Версия Java |
GC по умолчанию |
java 8 |
Parallel GC |
java 9+ |
G1 GC |
Некоторые JDK (например, Azul Zulu) могут использовать ZGC или Shenandoah по умолчанию, если заданы специфичные цели latency
Паузы
Stop-the-world – при некоторых фазах GC все потоки приостанавливаются.
Minor GC – в Young Generation.
Major/Full GC – в Old Generation, может включать очистку Metaspace.
Performance и тюнинг
-Xms/-Xmx– минимальный/максимальный размер heap.-Xss– размер стека на поток.-Xss– размер стека на поток.-XX:+PrintGCDetails,-Xlog:gc
JIT (Just-In-Time) компиляция
JVM сначала интерпретирует байт-код, но часто выполняемые методы компилируются в нативный машинный код во время работы программы.
Позволяет ускорить выполнение, избегая постоянной интерпретации.
-
Оптимизации
Inlining — метод “встраивается” в вызывающий код, чтобы убрать накладные расходы на вызов метода.
Escape Analysis — анализирует, покидает ли объект метод/поток; если нет — объект может быть размещен в стеке вместо кучи, что снижает нагрузку на GC.
Class Loading и Reflectio
Delegation Model
ClassLoader сначала делегирует загрузку родителю, и лишь потом пытается загрузить сам.
Custom ClassLoaders
Нужны для:
плагинов
модульных систем (OSGi, Spring Boot)
горячей перезагрузки кода
Reflection
Reflection позволяет динамически работать с классами и их членами, создавать прокси, но замедляет выполнение и может нарушать инкапсуляцию. SecurityManager может ограничивать такие операции.
Типы ссылок в java
Strong Reference (сильная ссылка)
Это обычные ссылки, которые мы используем каждый день.
Пока на объект существует хотя бы одна сильная ссылка — он не подлежит сборке мусора.
Чтобы объект мог быть собран, все сильные ссылки на него должны быть обнулены.
Object obj = new Object();
Soft Reference (мягкая ссылка)
Объект удаляется только при нехватке памяти.
Используется в кешах, чтобы не загружать память, но сохранить объект, если он ещё полезен.
Можно получить объект через
ref.get(), но если GC уже удалил его — вернётсяnull.
SoftReference<Object> ref = new SoftReference<>(new Object());
Weak Reference (слабая ссылка)
Объект может быть собран немедленно, даже если только слабые ссылки на него остались.
Используется для реализации структур с автоудалением (например,
WeakHashMap).Часто применяется, когда объект должен быть доступен «до тех пор, пока он кому-то нужен».
WeakReference<Object> ref = new WeakReference<>(new Object());
Phantom Reference (фантомная ссылка)
Объект уже помечен как удаляемый, но ещё не собран GC.
Метод
get()всегда возвращаетnull.Используется для контроля финализации и освобождения ресурсов вне heap (например, off-heap, native).
Требует
ReferenceQueue, через которую можно узнать, что объект вот-вот будет удалён.
PhantomReference<Object> ref = new PhantomReference<>(new Object(), referenceQueue);
Итог
Сегодня мы разобрали ключевые аспекты JVM, работу памяти и сборку мусора, которые чаще всего встречаются на собеседованиях по Java. Конечно, не обязательно, что спросят всё из этого списка, но с ним вы сможете уверенно обсуждать эти темы и точно не растеряетесь.
В будущем я планирую продолжать разбирать другие популярные темы на собеседованиях и делиться шпаргалками, чтобы ваша подготовка к собесу стала проще и эффективнее.
Всем спасибо за внимание, удачных собесов и хорошего дня!)
Elinkis
Жду 3 часть! Спасибо!