Введение
Виртуальная машина, которая запускает программы на Java.
Виртуальная машина Java (JVM) — это виртуальная машина, позволяющая компьютеру выполнять программы на Java, а также программы, написанные на других языках, которые компилируются в байт-код Java. JVM подробно описана в спецификации, которая формально определяет требования к реализации JVM. Наличие спецификации обеспечивает переносимость Java-программ между различными реализациями, избавляя разработчиков, использующих Java Development Kit (JDK), от необходимости учитывать особенности базовой аппаратной платформы. Эталонная реализация JVM разрабатывается проектом OpenJDK как открытый исходный код и включает в себя JIT-компилятор HotSpot. Коммерческие выпуски Java, предоставляемые Oracle, основаны на среде выполнения OpenJDK. Eclipse OpenJ9 — это еще одна JVM с открытым исходным кодом для OpenJDK.
Спецификация JVM
Виртуальная машина Java — это абстрактный (виртуальный) компьютер, определяемый спецификацией. Она является частью среды выполнения Java. Алгоритм сборки мусора и любая внутренняя оптимизация инструкций виртуальной машины Java (их преобразование в машинный код) не специфицированы. Основная причина этого – не создавать излишних ограничений для разработчиков. Любое Java-приложение может быть запущено только внутри конкретной реализации абстрактной спецификации виртуальной машины Java. Начиная с Java Platform, Standard Edition (J2SE) 5.0, изменения в спецификации JVM разрабатывались в рамках Java Community Process как JSR 924. По состоянию на 2006 год, изменения в спецификации для поддержки изменений, предложенных в формате файла класса (JSR 202), вносятся как обновление JSR 924. Спецификация для JVM была опубликована в виде «синей книги», в предисловии к которой говорится:
Мы намерены, чтобы эта спецификация достаточно полно документировала виртуальную машину Java, чтобы обеспечить возможность создания совместимых реализаций по принципу «чистой комнаты». Oracle предоставляет тесты для проверки корректной работы реализаций виртуальной машины Java. Одна из JVM от Oracle называется HotSpot, другая, унаследованная от BEA Systems, — JRockit. Oracle владеет товарным знаком Java и может разрешать его использование для сертификации комплектов реализации как полностью совместимых со спецификацией Oracle.
We intend that this specification should sufficiently document the Java Virtual Machine to make possible compatible clean room implementations. Oracle provides tests that verify the proper operation of implementations of the Java Virtual Machine. One of Oracle's JVMs is named HotSpot; the other, inherited from BEA Systems, is JRockit. Oracle owns the Java trademark and may allow its use to certify implementation suites as fully compatible with Oracle's specification.
Архитектура виртуальных машин
JVM работает с конкретными типами данных, как это определено в спецификациях Java Virtual Machine. Типы данных можно разделить на примитивные типы (целые числа, числа с плавающей точкой, long и т. д.) и ссылочные типы. Первые JVM были исключительно 32-битными машинами. Типы long и double, занимающие 64 бита, поддерживаются аппаратно, но потребляют две ячейки памяти в локальных переменных или стеке операндов кадра, поскольку размер каждой ячейки составляет 32 бита. Типы boolean, byte, short и char расширяются до 32 бит (за исключением char, который расширяется нулями) и обрабатываются как 32-битные целые числа, аналогично типу int. Младшие типы имеют лишь несколько специфичных для типа инструкций для загрузки, хранения и преобразования типов. Boolean обрабатывается как 8-битное байтовое значение, где 0 представляет false, а 1 – true. (Хотя boolean рассматривался как тип с момента выхода The Java Virtual Machine Specification, Second Edition, уточнившей этот вопрос, в скомпилированном и выполняемом коде разница между boolean и byte невелика, за исключением искажения имен в сигнатурах методов и типа массивов boolean. Boolean в сигнатурах методов искажаются как Z, а byte – как B. Массивы boolean имеют тип boolean[], но используют 8 бит на элемент, и JVM не имеет встроенных средств для упаковки boolean в битовый массив, поэтому, за исключением типа, они ведут себя и функционируют как массивы byte. Во всех остальных случаях тип boolean фактически неизвестен JVM, поскольку все инструкции для работы с boolean также используются для работы с byte.) Однако более новые версии JVM (OpenJDK HotSpot JVM) поддерживают 64-битную архитектуру, поэтому можно использовать 32-битную или 64-битную JVM на 64-битной ОС. Основное преимущество запуска Java в 64-битной среде – увеличенное адресное пространство. Это позволяет значительно увеличить размер кучи Java и максимальное количество Java-потоков, что необходимо для определенных крупных приложений; однако использование 64-битной JVM по сравнению с 32-битной может привести к снижению производительности. JVM имеет кучу с автоматической сборкой мусора для хранения объектов и массивов. Код, константы и другие данные классов хранятся в "области методов". Область методов логически является частью кучи, но реализации могут обрабатывать область методов отдельно от кучи и, например, не подвергать ее сборке мусора. Каждый поток JVM также имеет свой собственный стек вызовов (называемый "Стек виртуальной машины Java" для ясности), который хранит фреймы. Новый фрейм создается при каждом вызове метода и уничтожается при завершении этого метода. Каждый фрейм предоставляет "стек операндов" и массив "локальных переменных". Стек операндов используется для операндов при выполнении вычислений и для получения возвращаемого значения вызванного метода, в то время как локальные переменные выполняют ту же функцию, что и регистры, и также используются для передачи аргументов метода. Таким образом, JVM является одновременно стековой и регистровой машиной. На практике HotSpot устраняет все стеки, кроме стека нативного потока/вызовов, даже при работе в интерпретируемом режиме, поскольку его Templating Interpreter технически функционирует как компилятор.
Языки JVM
Язык JVM – это любой язык, функциональность которого может быть выражена посредством корректного файла класса, способного работать в виртуальной машине Java. Файл класса содержит инструкции виртуальной машины Java (байт-код Java) и таблицу символов, а также прочую дополнительную информацию. Формат файла класса – это аппаратная и операционно-независимая двоичная форма, используемая для представления скомпилированных классов и интерфейсов. Существует множество языков JVM, включая старые языки, портированные на JVM, и совершенно новые языки. JRuby и Jython, пожалуй, наиболее известные порты существующих языков, а именно Ruby и Python соответственно. Среди новых языков, созданных с нуля для компиляции в байт-код Java, наиболее популярными являются Clojure, Groovy, Scala и Kotlin. Важной особенностью языков JVM является их совместимость друг с другом, благодаря чему, например, библиотеки Scala можно использовать в программах на Java и наоборот. Java 7 JVM реализует JSR 292: Поддержка динамически типизированных языков на платформе Java – новую функцию, обеспечивающую поддержку динамически типизированных языков в JVM. Эта функция разрабатывается в рамках проекта Da Vinci Machine, целью которого является расширение возможностей JVM для поддержки языков, отличных от Java.
Интерпретатор байт-кода и компилятор "just-in-time"
Для каждой аппаратной архитектуры требуется свой интерпретатор байт-кода Java. Если компьютер оснащен интерпретатором байт-кода Java, он может выполнять любую программу на байт-коде Java, и одна и та же программа может быть запущена на любом компьютере, имеющем такой интерпретатор. Выполнение байт-кода Java интерпретатором всегда медленнее, чем выполнение той же программы, скомпилированной в машинный код. Эта проблема смягчается компиляторами Just-In-Time (JIT) для выполнения байт-кода Java. JIT-компилятор может переводить байт-код Java в машинный код во время выполнения программы. Переведенные части программы затем могут выполняться значительно быстрее, чем при интерпретации. Эта техника применяется к часто выполняемым частям программы. Таким образом, JIT-компилятор может значительно ускорить общее время выполнения. Нет никакой обязательной связи между языком программирования Java и байт-кодом Java. Программа, написанная на Java, может быть скомпилирована непосредственно в машинный язык конкретного компьютера, а программы, написанные на других языках, могут быть скомпилированы в байт-код Java. Байт-код Java предназначен для обеспечения платформенной независимости и безопасности. Некоторые реализации JVM не включают интерпретатор, а состоят только из JIT-компилятора.
JVM в веб-браузере
В начале жизненного цикла платформы Java, JVM позиционировалась как веб-технология для создания насыщенных веб-приложений. По состоянию на 2018 год большинство веб-браузеров и операционных систем, включающих веб-браузеры, не поставляются с плагином Java и не допускают установку сторонних плагинов, отличных от Flash. Плагин Java для браузера был объявлен устаревшим в JDK 9. Плагин Java для браузера NPAPI был разработан для выполнения так называемых Java-апплетов, встроенных в HTML-страницы. В браузерах с установленным плагином апплет может отображаться в выделенной прямоугольной области на странице. Поскольку плагин включает в себя JVM, Java-апплеты не ограничиваются языком программирования Java; любой язык, ориентированный на JVM, может выполняться в этом плагине. Ограниченный набор API предоставляет апплетам доступ к микрофону пользователя или 3D-ускорению, однако апплеты не могут изменять страницу за пределами выделенной области. Adobe Flash Player, основной конкурирующий продукт, работает аналогичным образом в этом отношении. По данным W3Techs на июнь 2015 года, использование Java-апплетов и Silverlight снизилось до 0,1% для всех веб-сайтов, в то время как использование Flash снизилось до 10,8%.
JavaScript JVM и интерпретаторы
С мая 2016 года JavaPoly позволяет пользователям импортировать Java-библиотеки без изменений и вызывать их напрямую из JavaScript. JavaPoly позволяет веб-сайтам использовать Java-библиотеки без изменений, даже если у пользователя не установлена Java на компьютере.
Транспиляция в JavaScript
С постоянным улучшением скорости выполнения JavaScript, в сочетании с растущим использованием мобильных устройств, веб-браузеры которых не поддерживают плагины, предпринимаются попытки охватить этих пользователей посредством транспиляции в JavaScript. Можно транспилировать как исходный код, так и байт-код JVM в JavaScript. Компиляция байт-кода JVM, который является универсальным для языков JVM, позволяет использовать существующий компилятор языка для генерации байт-кода. Основные транспиляторы байт-кода JVM в JavaScript включают TeaVM, компилятор, входящий в состав Dragome Web SDK, Bck2Brwsr и компилятор j2js. Ведущие транспиляторы с языков JVM в JavaScript включают транспилятор Java в JavaScript, входящий в состав Google Web Toolkit, Clojurescript (Clojure), GrooScript (Apache Groovy), Scala.js (Scala) и другие.