JVM
JVM (рус. виртуальная машина Java, англ. Java Virtual Machine) — программная виртуальная машина, являющаяся ядром среды выполнения Java (JRE). Она исполняет байт-код классов Java и ряд других языков, абстрагируя программный код от конкретного оборудования и операционной системы и тем самым реализуя принцип «Write Once, Run Anywhere» (WORA)[4].
Что важно знать
| JVM | |
|---|---|
| англ. Java Virtual Machine | |
| Область использования | Программирование, Виртуализация |
| Дата появления | 1995[1] |
| Место появления | Sun Microsystems[2] |
| Автор понятия | Джеймс Гослинг[3] |
Определение
JVM представляет собой многоуровневую программную платформу, которая:
- интерпретирует или JIT-компилирует Java-байт-код в машинные инструкции целевой платформы[5];
- динамически загружает классы в память и проводит их верификацию на корректность и безопасность[6];
- автоматически управляет памятью приложения с помощью встроенного сборщика мусора, предотвращая утечки памяти[7];
- предоставляет нативные механизмы многопоточности и синхронизации[8];
- обеспечивает обработку исключений и встроенные средства безопасности (верификация байт-кода, «песочница»)[9].
История
История создания JVM неразрывно связана с разработкой языка Java. В 1991 году в компании Sun Microsystems стартовал внутренний исследовательский проект под кодовым названием «Green Project». Его возглавил Джеймс Гослинг, а в команду разработчиков входили также Майк Шеридан и Патрик Ноутон[10]. Целью проекта было создание программной платформы для бытовой электроники, такой как интерактивное телевидение[11]. Основной проблемой была необходимость поддержки множества различных процессоров, что требовало переписывания кода для каждого устройства. Для решения этой задачи Гослинг предложил концепцию виртуальной машины — промежуточного программного слоя, который бы исполнял универсальный байт-код и транслировал его в инструкции для конкретной аппаратной платформы[12]. Первая реализация JVM была создана Гослингом в 1992 году, а язык, для которого она предназначалась, изначально носил название «Oak»[11][13].
Официальный публичный анонс языка Java и виртуальной машины Java состоялся 23 мая 1995 года на конференции SunWorld '95[10][14]. JVM стала ключевым компонентом новой платформы, реализуя принцип «Write Once, Run Anywhere» (WORA, «напиши один раз, запускай везде»). Вскоре после презентации компания Netscape объявила о намерении встроить поддержку Java в свой браузер, что способствовало быстрому росту популярности технологии[14].
В 1996 году Sun Microsystems выпустила первую официальную спецификацию JVM, известную как «Голубая книга JVM» (англ. The JVM Specification или «Blue Book»)[15]. Этот документ, авторами которого выступили Тим Линдхольм, Фрэнк Йеллин, Гилад Браха и Алекс Бакли, формализовал архитектуру и поведение виртуальной машины[16]. Публикация спецификации стала отраслевым стандартом и позволила сторонним разработчикам создавать собственные, совместимые с Java, реализации JVM[15].
В 2009—2010 годах компания Sun Microsystems была поглощена корпорацией Oracle, которая с тех пор владеет правами на торговую марку Java и продолжает развитие платформы, включая JVM[17].
Структурные элементы JVM
JVM логически делится на несколько подсистем и областей памяти[18][19]:
- Подсистема загрузчиков классов (англ. Class Loader Subsystem) — ищет, загружает и связывает *.class-файлы.
- Области данных времени выполнения (англ. Runtime Data Areas)
- англ. Method Area / англ. Metaspace — метаданные классов и пул констант.
- англ. Heap — область, в которой размещаются все объекты, создаваемые приложением.
- Стек JVM каждого потока (англ. JVM Language Stacks) — хранит кадры вызовов методов (фреймы), локальные переменные и промежуточные результаты вычислений.
- PC-регистр (англ. Program Counter Register) — содержит адрес текущей исполняемой инструкции для каждого потока.
- англ. Native Method Stack — стек для данных нативных (JNI) вызовов.
- Исполнительный механизм (англ. Execution Engine)
- Интерпретатор.
- JIT-компилятор.
- Подсистема сборки мусора.
- Интерфейс для вызова платформенно-ориентированного кода (англ. Java Native Interface, JNI) / англ. Java Native Access, JNA — интерфейс взаимодействия с нативными библиотеками.
В рамках развития платформы в HotSpot JVM была представлена функция компактных заголовков объектов (англ. Compact Object Headers). Она появилась как экспериментальная в JDK 24 (JEP 450)[20] и стала стабильной в JDK 25 (JEP 519)[21]. Эта функция позволяет уменьшить размер заголовка объекта на 64-битных архитектурах с 12–16 байт до 8 байт[22]. Такое изменение приводит к сокращению потребления памяти в куче, что, в свою очередь, может снизить частоту сборок мусора и повысить общую производительность приложения[23][24].
Загрузчик классов
Подсистема включает три иерархически связанных загрузчика:[25]
- Bootstrap ClassLoader — загружает базовые классы JDK из
JAVA_HOME/lib. - Platform (Class Path) ClassLoader — подключает расширенные модули JDK.
- Application ClassLoader — читает классы приложения из переменной
CLASSPATH.
Модель делегирования предусматривает передачу запроса родительскому загрузчику до попытки загрузки в дочернем.
Среда выполнения
Сборщик мусора
Автоматическая сборка мусора освобождает память от недостижимых объектов, опираясь на поколения кучи и алгоритм «пометка – очистка – компактация». В HotSpot доступны сборщики Serial GC, Parallel GC, CMS, G1 (по умолчанию с JDK 9), а также низкопаузные ZGC и Shenandoah[27][28].
В период с 2020 по 2025 год развитие сборщиков мусора было сосредоточено на улучшении производительности и снижении пауз в низкозатратных сборщиках ZGC и Shenandoah, а также на внедрении в них поддержки поколений.
ZGC (Z Garbage Collector) и Shenandoah были переведены из экспериментального статуса в полноценный в JDK 15 (сентябрь 2020 года)[29][30]. В JDK 16 ZGC получил обновление (JEP 376), которое переместило обработку стеков потоков в конкурентную фазу, позволив достигать пауз длительностью менее одной миллисекунды[31]. Ключевым нововведением стало появление поколенческого ZGC (англ. Generational ZGC) в JDK 21 (JEP 439), что позволило снизить накладные расходы процессора и требования к объёму кучи[32]. Начиная с JDK 24, не-поколенческий режим ZGC был удалён (JEP 490), и поколенческий режим стал единственным доступным вариантом[33].
Для Shenandoah поддержка поколений была представлена как экспериментальная функция в JDK 24 (JEP 404)[34] и стала полноценной в JDK 25 (JEP 521)[35]. Сборщик G1, оставаясь стандартным, также получил улучшения: в JDK 18 были значительно сокращены его накладные расходы на нативную память[36], а в JDK 21 добавлена возможность перемещать «гигантские» объекты (англ. Humongous Objects) во время полной сборки для борьбы с фрагментацией памяти[37].
Этапы работы JVM
При запуске приложения виртуальная машина проходит серию фаз:[38]
- загрузка класса;
- связывание (проверка, подготовка, резольвинг);
- инициализация;
- исполнение байт-кода;
- регулярные циклы сборки мусора.
Загрузка происходит лениво, при первом обращении к типу. Подсистема Class Loader формирует объект Class и сохраняет его метаданные в Metaspace[6].
На этапе проверки байт-код валидируется на предмет корректности стека и доступа. Подготовка выделяет память под статические поля и присваивает значения по умолчанию, а резольвинг заменяет символические ссылки из пула констант прямыми адресами[39].
JVM исполняет статические блоки и устанавливает заданные разработчиком значения статических полей. Инициализация суперкласса всегда предшествует инициализации подкласса[40].
Execution Engine читает инструкции, интерпретирует их либо передаёт «горячие» участки в JIT-компилятор, преобразующий их в нативный код для повышения производительности[41].
GC-поток (или потоки) периодически помечают недостижимые объекты и освобождают их память; при необходимости выполняется компактизация для устранения фрагментации[42].
Сборка мусора
- Minor GC — быстрая очистка молодого поколения (Eden + Survivor Spaces).
- Major / Full GC — обработка старого поколения и, опционально, всей кучи[43].
Преимущества и недостатки
Ключевые характеристики JVM порождают как сильные, так и слабые стороны.
Сферы применения
- Серверная веб-разработка (Tomcat, Jetty, Spring).
- Корпоративные информационные системы (Java EE / Jakarta EE).
- Android (Dalvik / ART основаны на концепции JVM).
- Научно-вычислительные и финансовые приложения.
- Встроенные и IoT-устройства.
- Контейнеризированные облачные микросервисы[44].
Инструменты для работы с JVM
- JDK — полный комплект для разработки, включающий компилятор
javac, отладчикjdbи т.д. - JRE — минимальный набор для запуска приложений (JVM + стандартные библиотеки)[50].
Реализации JVM
- HotSpot (Oracle / OpenJDK) — эталонная реализация с адаптивной JIT-оптимизацией[51].
- Eclipse OpenJ9 — лёгкая и быстрая JVM от IBM/Eclipse, оптимизированная под контейнеры[52].
- GraalVM — поддерживает многозначные языки и AOT-компиляцию в нативные образы[53].
- Dalvik / ART — среда выполнения Android, использующая формат DEX и смешанную AOT/JIT-схему[54].
Средства мониторинга и профилирования JVM
- JConsole — встроенный JMX-клиент для мониторинга памяти, потоков и MBeans[55].
- VisualVM — графический анализатор CPU, памяти, heap / thread dump[56].
- Java Mission Control (JMC) и Java Flight Recorder (JFR) — инструменты для низкозатратного сбора и анализа телеметрии приложения[57]. Начиная с JDK 25, JFR получил значительные улучшения:
- JFR Method Timing & Tracing (JEP 520) — добавлены новые события (jdk.MethodTiming и jdk.MethodTrace) для профилирования времени выполнения и трассировки вызовов конкретных методов без изменения кода приложения[58].
- JFR CPU-Time Profiling (JEP 509) — представлен экспериментальный механизм для более точного профилирования использования процессорного времени на уровне ядра Linux[58].
- JFR Cooperative Sampling (JEP 518) — улучшена стабильность сбора сэмплов стеков вызовов, что повышает надёжность профилирования[59].
- JProfiler и YourKit — коммерческие профилировщики с детальным CPU/Memory анализом[60].
Инструменты отладки JVM
- jdb — консольный отладчик JDK.
- JDWP — протокол удалённой отладки.
- JDI — высокоуровневый Java-API отладчика.
- JVMTI — нативный интерфейс инструментов для тонкой интроспекции и управления исполняющей VM[61].
Интеграция JVM с другими платформами
- .NET — IKVM.NET (запуск Java-кода в CLR).
- Python — Jython (Python 2 на JVM) и Py4J (RPC-мост).
- Node.js — J2V8 / node-java (биндинги V8 через JNI).
- R — пакет rJava и проект Renjin.
- C/C++ — JNI и Invocation API, позволяющие встраивать JVM в нативные приложения[62].
Современное развитие и новые возможности
Развитие JVM в 2024—2025 годах, в рамках релизов JDK 24 и JDK 25 (LTS), было сосредоточено на повышении производительности, сокращении потребления памяти и внедрении новых API для упрощения разработки высоконагруженных приложений.
Ключевые API и языковые функции:
- Scoped Values (англ. Scoped Values) — стали финальной функцией в JDK 25 (JEP 506). Они предоставляют механизм для обмена неизменяемыми данными внутри одного потока и между дочерними потоками, особенно при использовании виртуальных потоков (Project Loom). Scoped Values являются более производительной и надёжной альтернативой механизму ThreadLocal.
- Vector API — продолжил развитие (десятая инкубация в JDK 25, JEP 508). Этот API позволяет выполнять векторные вычисления, которые во время выполнения компилируются в оптимальные SIMD-инструкции (англ. Single Instruction, Multiple Data) на поддерживаемых архитектурах процессоров, что обеспечивает производительность, значительно превосходящую скалярные вычисления.
- Структурная многопоточность (англ. Structured Concurrency) — остаётся в статусе предварительного просмотра (англ. Preview) в JDK 25 (JEP 505). API упрощает многопоточное программирование, позволяя рассматривать группу задач, выполняющихся в разных потоках, как единое целое, что облегчает обработку ошибок и отмену операций.
На уровне самой JVM были внедрены значительные оптимизации. Функция компактных заголовков объектов (англ. Compact Object Headers), представленная как экспериментальная в JDK 24 (JEP 450) и ставшая стабильной в JDK 25 (JEP 519), позволяет уменьшить размер заголовка объекта на 64-битных архитектурах. Это приводит к сокращению потребления памяти в куче и, как следствие, к снижению частоты сборок мусора. Для ускорения запуска приложений в JDK 25 были улучшены механизмы AOT-компиляции (JEP 514), что упростило создание и использование AOT-кэшей через командную строку.
Важным шагом в эволюции платформы стал полный отказ от поддержки 32-битных операционных систем на архитектуре x86 в JDK 25 (JEP 503). Ранее, в JDK 24, был окончательно удалён устаревший механизм Security Manager (JEP 486)[63].


