Виртуальная машина Java ( JVM ) — это виртуальная машина , которая позволяет компьютеру запускать программы Java , а также программы, написанные на других языках , которые также скомпилированы в байт-код Java . JVM подробно описана в спецификации , которая формально описывает, что требуется от реализации JVM. Наличие спецификации обеспечивает совместимость программ Java в различных реализациях, поэтому авторам программ, использующим Java Development Kit (JDK), не нужно беспокоиться об особенностях базовой аппаратной платформы.
Эталонная реализация JVM разработана проектом OpenJDK как открытый исходный код и включает JIT-компилятор под названием HotSpot . Коммерчески поддерживаемые выпуски Java, доступные от Oracle , основаны на среде выполнения OpenJDK. Eclipse OpenJ9 — еще одна JVM с открытым исходным кодом для OpenJDK.
Виртуальная машина Java — это абстрактный (виртуальный) компьютер, определенный спецификацией. Это часть среды выполнения Java. Используемый алгоритм сборки мусора и какая-либо внутренняя оптимизация инструкций виртуальной машины Java (их перевод в машинный код ) не указаны. Основная причина этого упущения состоит в том, чтобы не ограничивать разработчиков без необходимости. Любое Java-приложение может быть запущено только внутри некоторой конкретной реализации абстрактной спецификации виртуальной машины Java. [2]
Начиная с платформы Java Standard Edition (J2SE) 5.0, изменения в спецификации JVM были разработаны в рамках процесса сообщества Java как JSR 924. [3] С 2006 года [обновлять]внесены изменения в спецификацию для поддержки изменений, предложенных в формате файла класса ( JSR 202) [4] выполняются как поддерживающая версия JSR 924. Спецификация JVM была опубликована в виде синей книги [5] , в предисловии которой говорится:
Мы предполагаем, что эта спецификация должна достаточно документировать виртуальную машину Java, чтобы сделать возможными совместимые реализации для «чистых помещений». Oracle предоставляет тесты, проверяющие правильную работу реализаций виртуальной машины Java.
Одна из JVM Oracle называется HotSpot; другой, унаследованный от BEA Systems , — JRockit . Oracle владеет торговой маркой Java и может разрешить ее использование для сертификации пакетов реализации как полностью совместимых со спецификацией Oracle.
Одной из организационных единиц байт-кода JVM является класс . Реализация загрузчика классов должна иметь возможность распознавать и загружать все, что соответствует формату файлов классов Java . Любая реализация может распознавать и другие двоичные формы, помимо файлов классов , но она должна распознавать файлы классов .
Загрузчик классов выполняет три основных действия в строгом порядке:
В общем, существует три типа загрузчика классов: загрузчик классов начальной загрузки, загрузчик классов расширения и загрузчик классов системы/приложения.
Каждая реализация виртуальной машины Java должна иметь загрузчик классов начальной загрузки, способный загружать доверенные классы, а также загрузчик классов расширения или загрузчик классов приложений. Спецификация виртуальной машины Java не определяет, как загрузчик классов должен находить классы.
JVM работает с определенными типами данных, как указано в спецификациях виртуальной машины Java. Типы данных можно разделить [6] на примитивные типы ( целые числа , с плавающей запятой, длинные и т. д.) и ссылочные типы. Более ранние JVM были только 32-битными машинами. long
и double
типы, которые являются 64-битными , поддерживаются изначально, но занимают две единицы памяти в локальных переменных кадра или стеке операндов, поскольку каждая единица имеет размер 32 бита. Все типы boolean
, byte
, short
и char
типы расширяются знаком (за исключением char
расширенного нулями ) и работают как 32-битные целые числа, так же, как и int
типы. Меньшие типы имеют лишь несколько специфичных для типа инструкций по загрузке, хранению и преобразованию типов. boolean
обрабатывается как 8-битные byte
значения, где 0 представляет собой false
, а 1 представляет true
. (Хотя со времен Спецификации виртуальных машин Java во втором изданииboolean
этот вопрос рассматривается как тип , в скомпилированном и исполняемом коде между a и a существует небольшая разница, за исключением искажения имен в сигнатурах методов и типах логических массивов. s in сигнатуры методов искажаются, как и s, искажаются как . Логические массивы содержат тип , но используют 8 бит на элемент, а JVM не имеет встроенной возможности упаковывать логические значения в битовый массив , поэтому, за исключением типа, который они выполняют и ведут себя то же самое, что и массивы.Во всех других случаях тип фактически неизвестен JVM, поскольку все инструкции для работы с логическими значениями также используются для работы с s.) Однако более новые версии JVM (OpenJDK HotSpot JVM) поддерживают 64-разрядную версию, поэтому вы может иметь 32-битную/64-битную JVM в 64-битной ОС. Основное преимущество запуска Java в 64-битной среде — большее адресное пространство. Это позволяет значительно увеличить размер кучи Java и увеличить максимальное количество потоков Java, что необходимо для определенных типов крупных приложений; однако при использовании 64-битной JVM производительность снижается по сравнению с 32-битной JVM.boolean
byte
boolean
Z
byte
B
boolean[]
byte
boolean
byte
В JVM есть куча со сбором мусора для хранения объектов и массивов. Код, константы и другие данные класса хранятся в «области метода». Область метода логически является частью кучи, но реализации могут обрабатывать область метода отдельно от кучи и, например, не выполнять сборку мусора. Каждый поток JVM также имеет свой собственный стек вызовов (для ясности называемый «стеком виртуальной машины Java»), в котором хранятся кадры . Новый кадр создается каждый раз при вызове метода и уничтожается при выходе из этого метода.
Каждый кадр предоставляет «стек операндов» и массив «локальных переменных». Стек операндов используется для операндов для выполнения вычислений и для получения возвращаемого значения вызываемого метода, а локальные переменные служат той же цели, что и регистры , а также используются для передачи аргументов метода. Таким образом, JVM является одновременно стековой машиной и регистровой машиной . На практике HotSpot полностью исключает каждый стек, кроме собственного стека потоков/вызовов, даже при работе в интерпретируемом режиме, поскольку его интерпретатор шаблонов технически функционирует как компилятор.
В JVM есть инструкции для следующих групп задач:
Целью является бинарная совместимость. Каждая конкретная операционная система хоста нуждается в собственной реализации JVM и среде выполнения. Эти JVM интерпретируют байт-код семантически одинаково, но фактическая реализация может отличаться. Более сложной, чем просто эмуляция байт-кода, является совместимая и эффективная реализация основного API Java , который должен быть сопоставлен с каждой операционной системой хоста.
Эти инструкции оперируют набором общихабстрактные типы данных , а не собственные типы данных любой конкретной архитектуры набора команд .
Язык JVM — это любой язык, функциональность которого может быть выражена в виде допустимого файла класса, который может размещаться на виртуальной машине Java. Файл класса содержит инструкции виртуальной машины Java ( байт-код Java ) и таблицу символов, а также другую вспомогательную информацию. Формат файла классов — это двоичный формат, независимый от аппаратного обеспечения и операционной системы, используемый для представления скомпилированных классов и интерфейсов. [7]
Существует несколько языков JVM: как старые языки, портированные на JVM, так и совершенно новые языки. JRuby и Jython , пожалуй, самые известные порты существующих языков, т.е. Ruby и Python соответственно. Из новых языков, созданных с нуля для компиляции в байт-код Java, наиболее популярными могут быть Clojure , Groovy , Scala и Kotlin . Примечательной особенностью языков JVM является то, что они совместимы друг с другом , так что, например, библиотеки Scala можно использовать с программами Java и наоборот. [8]
Java 7 JVM реализует JSR 292: Поддержка динамически типизированных языков [9] на платформе Java, новую функцию, которая поддерживает динамически типизированные языки в JVM. Эта функция разработана в рамках проекта Da Vinci Machine , цель которого — расширить JVM, чтобы она поддерживала другие языки, кроме Java. [10] [11]
Основная философия Java заключается в том, что она по своей сути безопасна с точки зрения того, что ни одна пользовательская программа не может привести к сбою хост-машины или иным образом ненадлежащим образом вмешиваться в другие операции на хост-машине, и что можно защитить определенные методы и структуры данных, принадлежащие доверенным лицам. код от доступа или повреждения ненадежным кодом, выполняющимся в той же JVM. Кроме того, не допускается возникновение распространенных ошибок программиста, которые часто приводят к повреждению данных или непредсказуемому поведению, например, к доступу к концу массива или использованию неинициализированного указателя. Несколько функций Java в совокупности обеспечивают такую безопасность, включая модель классов, кучу мусора и верификатор.
JVM проверяет весь байт-код перед его выполнением. Эта проверка состоит в основном из трех типов проверок:
Первые две из этих проверок происходят в основном на этапе проверки, который происходит, когда класс загружается и становится пригодным для использования. Третий в основном выполняется динамически, когда к элементам данных или методам класса впервые обращается другой класс.
Верификатор допускает только некоторые последовательности байт-кода в допустимых программах, например, инструкция перехода (ветви) может быть нацелена только на инструкцию внутри того же метода . Более того, верификатор гарантирует, что любая данная инструкция работает в фиксированном месте стека, [12] позволяя JIT-компилятору преобразовывать доступ к стеку в доступ к фиксированному регистру. По этой причине тот факт, что JVM представляет собой стековую архитектуру, не подразумевает снижения скорости эмуляции в архитектурах на основе регистров при использовании JIT-компилятора. В условиях архитектуры JVM с верификацией кода для JIT-компилятора не имеет значения, получает ли он именованные мнимые регистры или воображаемые позиции стека, которые должны быть выделены регистрам целевой архитектуры. Фактически, проверка кода отличает JVM от классической стековой архитектуры, эффективная эмуляция которой с помощью JIT-компилятора более сложна и обычно выполняется более медленным интерпретатором. Кроме того, интерпретатор, используемый JVM по умолчанию, представляет собой специальный тип, известный как интерпретатор шаблонов, который преобразует байт-код непосредственно в собственный машинный язык на основе регистров, а не эмулирует стек, как типичный интерпретатор. [13] Во многих аспектах HotSpot Interpreter можно считать JIT-компилятором, а не настоящим интерпретатором, то есть архитектура стека, на которую нацелен байт-код, на самом деле не используется в реализации, а просто спецификация для промежуточного представления, которое вполне может быть реализовано. в архитектуре на основе регистров. Другим примером архитектуры стека, являющейся просто спецификацией и реализованной в виртуальной машине на основе регистров, является Common Language Runtime . [14]
В исходной спецификации средства проверки байт-кода использовался естественный язык, который в некоторых отношениях был неполным или неправильным. Был предпринят ряд попыток определить JVM как формальную систему. Благодаря этому можно более тщательно проанализировать безопасность текущих реализаций JVM и предотвратить потенциальные уязвимости безопасности. Также можно будет оптимизировать JVM, пропустив ненужные проверки безопасности, если безопасность запускаемого приложения будет доказана. [15]
Архитектура виртуальной машины обеспечивает очень детальный контроль над действиями, которые разрешено выполнять коду внутри машины. Он предполагает, что код «семантически» корректен, то есть он успешно прошел (формальный) процесс проверки байт-кода, материализуемый инструментом, возможно, вне виртуальной машины. Это сделано для обеспечения безопасного выполнения ненадежного кода из удаленных источников, модели, используемой Java-апплетами , и других безопасных загрузок кода. После проверки байт-кода загруженный код запускается в ограниченной « песочнице », которая предназначена для защиты пользователя от неправильного поведения или вредоносного кода. В дополнение к процессу проверки байт-кода издатели могут приобрести сертификат, с помощью которого можно будет ставить цифровую подпись апплетам как безопасным, что дает им разрешение просить пользователя выйти из «песочницы» и получить доступ к локальной файловой системе, буферу обмена , запускать внешние части программного обеспечения. , или сеть.
Формальное доказательство верификаторов байт-кода было выполнено индустрией Javacard (формальная разработка встроенного верификатора для байт-кода карты Java [16] ).
Для каждой аппаратной архитектуры необходим свой интерпретатор байт-кода Java . Если на компьютере есть интерпретатор байт-кода Java, он может запускать любую программу байт-кода Java, и одну и ту же программу можно запускать на любом компьютере, имеющем такой интерпретатор.
Когда байт-код Java выполняется интерпретатором, выполнение всегда будет медленнее, чем выполнение той же программы, скомпилированной на родном машинном языке. Эту проблему смягчают JIT-компиляторы для выполнения байт-кода Java. JIT-компилятор может транслировать байт-код Java на собственный машинный язык во время выполнения программы. Переведенные части программы могут выполняться гораздо быстрее, чем интерпретироваться. Этот метод применяется к часто выполняемым частям программы. Таким образом, JIT-компилятор может значительно ускорить общее время выполнения.
Между языком программирования Java и байт-кодом Java нет необходимой связи. Программа, написанная на Java, может быть скомпилирована непосредственно в машинный язык реального компьютера, а программы, написанные на других языках, кроме Java, могут быть скомпилированы в байт-код Java.
Байт-код Java должен быть независимым от платформы и безопасным. [17] Некоторые реализации JVM не включают интерпретатор, а состоят только из JIT-компилятора. [18]
В начале существования платформы Java JVM позиционировалась как веб-технология для создания насыщенных веб-приложений . По состоянию на 2018 год [обновлять]большинство веб-браузеров и операционных систем, входящих в состав веб-браузеров, не поставляются с подключаемым модулем Java и не допускают неопубликованную загрузку каких-либо подключаемых модулей, отличных от Flash . Плагин браузера Java устарел в JDK 9. [19]
Плагин браузера Java NPAPI был разработан, чтобы позволить JVM выполнять так называемые Java-апплеты , встроенные в HTML-страницы. В браузерах с установленным плагином апплету разрешено рисовать в прямоугольной области назначенной ему страницы. Поскольку плагин включает в себя JVM, Java-апплеты не ограничиваются языком программирования Java; любой язык, предназначенный для JVM, может работать в плагине. Ограниченный набор API позволяет апплетам получать доступ к микрофону пользователя или 3D-ускорению, хотя апплеты не могут изменять страницу за пределами ее прямоугольной области. Adobe Flash Player , основная конкурирующая технология, в этом отношении работает таким же образом.
[обновлять]По данным W3Techs, по состоянию на июнь 2015 года использование Java-апплетов и Silverlight упало до 0,1% для всех веб-сайтов, а использование Flash упало до 10,8%. [20]
С мая 2016 года JavaPoly позволяет пользователям импортировать неизмененные библиотеки Java и вызывать их непосредственно из JavaScript. JavaPoly позволяет веб-сайтам использовать неизмененные библиотеки Java, даже если на компьютере пользователя не установлена Java. [21]
В связи с продолжающимся улучшением скорости выполнения JavaScript в сочетании с увеличением использования мобильных устройств, веб-браузеры которых не поддерживают плагины, предпринимаются усилия по таргетированию этих пользователей путем транспиляции в JavaScript. Можно перенести исходный код или байт-код JVM в JavaScript.
Компиляция байт-кода JVM, универсального для всех языков JVM, позволяет использовать существующий компилятор языка для создания байт-кода. Основным байт-кодом JVM для транспиляторов JavaScript являются TeaVM, [22] компилятор, содержащийся в Dragome Web SDK, [23] Bck2Brwsr, [24] и j2js-компилятор. [25]
К ведущим транспиляторам из языков JVM в JavaScript относятся транспилятор Java-to-JavaScript, содержащийся в Google Web Toolkit , Clojurescript (Clojure), GrooScript (Apache Groovy), Scala.js (Scala) и других. [26]