Всем привет!

Я работаю 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. Конечно, не обязательно, что спросят всё из этого списка, но с ним вы сможете уверенно обсуждать эти темы и точно не растеряетесь.

В будущем я планирую продолжать разбирать другие популярные темы на собеседованиях и делиться шпаргалками, чтобы ваша подготовка к собесу стала проще и эффективнее.

Всем спасибо за внимание, удачных собесов и хорошего дня!)

Комментарии (1)


  1. Elinkis
    17.11.2025 11:15

    Жду 3 часть! Спасибо!