что такое jit компиляция java
Введение в JIT компиляцию
Виртуальная машина Java HotSpot VM (доставшаяся Oracle после приобретения компании Sun Microsystems) составляет основу как для виртуальной машины Java (JVM), так и для OpenJDK (проект с открытым исходным кодом). Как и все виртуальные машины Java HotSpot VM обеспечивает необходимую среду для выполнения байт-кода. На практике она отвечает за три основные функции:
Эта статья фокусируется на интерпретации байт-кода, а именно на оптимизациях, проводимых в виртуальной машине.
JIT-компиляция
Java HotSpot VM помимо непосредственной интерпретации байт-кода может выполнять компиляцию байт-кода (отдельных методов целиком) в машинные инструкции для ускорения процесса выполнения.
Основное различие между этими двумя режимами заключается в том, что в режиме server выполняются более агрессивные оптимизации, основанные на предположениях, которые не всегда могут быть выполнены. Для оптимизации всегда проверяется верно ли соответствующее предположение об оптимизации. Если по каким-то причинам предположение не действительно, Java HotSpot VM откатывает оптимизацию и возвращает метод в режим интерпретации байт-кода. Такое поведение означает, что Java HotSpot VM никогда не сделает неверную оптимизацию.
Может показаться заманчивым уменьшить порог компиляции до очень маленького значения. Однако, это может привести к падению производительности, так как будет затрачено время на компиляцию методов не выполняющихся так часто чтобы покрыть накладные расходы на их компиляцию.
Наибольшей эффективности Java HotSpot VM достигает тогда, когда она может собрать достаточно статистики, чтобы принять разумное решение о том, что скомпилировать. Если вы уменьшите порог компиляции, Java HotSpot VM может потратить огромное количество времени компилируя методы, которые выполняются не так часто. Некоторые оптимизации выполняются только тогда, когда достаточно статистики было собрано. Так что код может быть не таким оптимальным каким бы он мог быть.
В Java HotSpot VM есть множество параметров, которые увеличивают количество выводимой о JIT информации. Наиболее часто используется PrintCompilation (который мы уже видели), но есть несколько других.
Мы будем использовать PrintCompilation для наблюдения за эффектами компиляции методов в Java HotSpot VM во время исполнения. Но для начала нужно сказать пару слов о методе System.nanoTime() для замера времени.
Таймеры
В Java мы можем получить доступ к двум таймерам: currentTimeMillis() и nanoTime(). Первый достаточно близко соответствует времени, которое мы наблюдаем в физическом мире. Его разрешения достаточно для большинства целей, но не для приложений с низкой задержкой.
В связи с очень высоким разрешением наносекундного таймера требуется с осторожностью обращаться с ним.
Например, currentTimeMillis() как правило синхронизировано между машинами достаточно хорошо и может использоваться для замера сетевых задержек. Но nanoTime() не обладает таким свойством.
Встраивание методов
JIT-компилятор может выполнять постепенное встраивание, то есть начать с встраивания простых методов и затем переходить на большие и большие блоки кода пока другие оптимизации не станут возможными.
Рассмотрим следующий код, сравнивающий производительность различных способов доступа к полям:
Объединение JVM
Скомпилируем эти классы и выполним тестирование:
на моей машине (2.8 GHz Intel Core i7, MacOS X 10.7) вывод был таким:
Что все это значит? Числа в первом столбце показывают время в миллисекундах с момента старта программы. Во втором столбце отображается ID метода (для скомпилированных методов) или количество итераций, выполненых в тесте.
Обратите внимание, что метод hashCode класса String в тесте непосредственно не использовался, но все же был скомпилирован, так как он использовался самой платформой.
Во 2-й строке можно увидеть, что оба способа обращения к полю довольно медленные, так как при первом запуске необходимо было выполнить загрузку соответствующих классов. В следующей строке мы видим, что тест выполнился значительно быстрее хотя никакой компиляции еще не произошло.
Также заметим следующее:
Важно отметить, что в установившемся состоянии выполнения доступ к полям напрямую или через методы get/set выполняются одинаково, так как методы были встроены в методы класса GetSetCaller. Таким образом код в классе GetSetCaller выполняет те же действия, что и код в классе DFACaller.
JIT-компиляция выполняется в фоне именно тогда, когда определенная оптимизация становится возможной для выполнения (изменяясь от машины к машине и реже от запуска к запуску).
Заключение
В этой статье была рассмотрена только верхушка айсберга JIT-компиляции в Java HotSpot VM. В частности не были отражены важные аспекты написания хороших тестов и то как использовать статистику, чтобы гарантировать, что динамическая природа платформы не дурит нас.
Тесты, использовавшиеся здесь довольно простые и вряд ли подойдут для реальных замеров. Во второй части статьи будет показано как поставить более реалистичные тесты и детально рассмотрен код, производимый JIT-компилятором.
Это вольный перевод статьи Introduction to JIT Compilation in Java HotSpot VM Бена Эванса и Питера Лоурея, опубликованной в майском номере журнала Java Magazine.
Java HotSpot JIT компилятор — устройство, мониторинг и настройка (часть 1)
AOT и JIT компиляторы
Процессоры могут исполнять только ограниченный набор инструкций — машинный код. Для исполнения программы процессором, она должна быть представлена в виде машинного кода.
Существуют компилируемые языки программирования, такие как C и C++. Программы, написанные на этих языках, распространяются в виде машинного кода. После того, как программа написана, специальный процесс — Ahead-of-Time (AOT) компилятор, обычно называемый просто компилятором, транслирует исходный код в машинный. Машинный код предназначен для выполнения на определенной модели процессора. Процессоры с общей архитектурой могут выполнять один и тот же код. Более поздние модели процессора как правило поддерживают инструкции предыдущих моделей, но не наоборот. Например, машинный код, использующий AVX инструкции процессоров Intel Sandy Bridge не может выполняться на более старых процессорах Intel. Существуют различные способы решения этой проблемы, например, вынесение критичных частей программы в библиотеку, имеющую версии под основные модели процессора. Но часто программы просто компилируются для относительно старых моделей процессоров и не используют преимущества новых наборов инструкций.
В противоположность компилируемым языкам программирования существуют интерпретируемые языки, такие как Perl и PHP. Один и тот же исходный код при таком подходе может быть запущен на любой платформе, для которой существует интерпретатор. Минусом этого подхода является то, что интерпретируемый код работает медленнее, чем машинный код, делающий тоже самое.
Язык Java предлагает другой подход, нечто среднее между компилируемыми и интерпретируемыми языками. Приложения на языке Java компилируются в промежуточный низкоуровневый код — байт-код (bytecode).
Название байт-код было выбрано потому, что для кодирования каждой операции используется ровно один байт. В Java 10 существует около 200 операций.
Байт-код затем исполняется JVM также как и программа на интерпретируемом языке. Но поскольку байт-код имеет строго определенный формат, JVM может компилировать его в машинный код прямо во время выполнения. Естественно, старые версии JVM не смогут сгенерировать машинный код, использующий новые наборы инструкций процессоров вышедших после них. С другой стороны, для того, чтобы ускорить Java-программу, ее даже не надо перекомпилировать. Достаточно запустить ее на более новой JVM.
HotSpot JIT компилятор
Единица скомпилированного кода называется nmethod (сокращение от native method).
Многоуровневая компиляция (tiered compilation)
На самом деле в HotSpot JVM существует не один, а два компилятора: C1 и C2. Другие их названия клиентский (client) и серверный (server). Исторически C1 использовался в GUI приложениях, а C2 в серверных. Отличаются компиляторы тем, как быстро они начинают компилировать код. C1 начинает компилировать код быстрее, в то время как C2 может генерировать более оптимизированный код.
Существует 5 уровней компиляции:
Последовательность | Описание |
---|---|
0-3-4 | Интерпретатор, уровень 3, уровень 4. Наиболее частый случай. |
0-2-3-4 | Случай, когда очередь уровня 4 (C2) переполнена. Код быстро компилируется на уровне 2. Как только профилирование этого кода завершится, он будет скомпилирован на уровне 3 и, наконец, на уровне 4. |
0-2-4 | Случай, когда очередь уровня 3 переполнена. Код может быть готов к компилированию на уровне 4 все еще ожидая своей очереди на уровне 3. Тогда он быстро компилируется на уровне 2 и затем на уровне 4. |
0-3-1 | Случай простых методов. Код сначала компилируется на уровне 3, где становится понятно, что метод очень простой и уровень 4 не сможет скомпилировать его оптимальней. Код компилируется на уровне 1. |
0-4 | Многоуровневая компиляция выключена. |
Code cache
Машинный код, скомпилированный JIT компилятором, хранится в области памяти называемой code cache. В ней также хранится машинный код самой виртуальной машины, например, код интерпретатора. Размер этой области памяти ограничен, и когда она заполняется, компиляция прекращается. В этом случае часть «горячих» методов так и продолжит выполняться интерпретатором. В случае переполнения JVM выводит следующее сообщение:
Другой способ узнать о переполнении этой области памяти — включить логирование работы компилятора (как это сделать обсуждается ниже).
Code cache настраивается также как и другие области памяти в JVM. Первоначальный размер задаётся параметром -XX:InitialCodeCacheSize. Максимальный размер задается параметром -XX:ReservedCodeCacheSize. По умолчанию начальный размер равен 2496 KB. Максимальный размер равен 48 MB при выключенной многоуровневой компиляции и 240 MB при включенной.
Начиная с Java 9 code cache разделен на 3 сегмента (суммарный размер по-прежнему ограничен пределами, описанными выше):
Мониторинг работы компилятора
Включить логирование процесса компиляции можно флагом -XX:+PrintCompilation (по умолчанию он выключен). При установке этого флага JVM будет выводить в стандартный поток вывода (STDOUT) сообщение каждый раз после компиляции метода или цикла. Большинство сообщений имеют следующий формат: timestamp compilation_id attributes tiered_level method_name size deopt.
Поле timestamp — это время со старта JVM.
Поле compilation_id — это внутренний ID задачи. Обычно он последовательно увеличивается в каждом сообщении, но иногда порядок может нарушаться. Это может произойти в случае, если существует несколько потоков компиляции работающих параллельно.
Поле attributes — это набор из пяти символов, несущих дополнительную информацию о скомпилированном коде. Если какой-то из атрибутов не применим, вместо него выводится пробел. Существуют следующие атрибуты:
Атрибут «b» означает, что компиляция произошла не в фоне, и не должен встречаться в современных версиях JVM.
Атрибут «n» означает, что скомпилированный метод является оберткой нативного метода.
Поле tiered_level содержит номер уровня, на котором был скомпилирован код или может быть пустым, если многоуровневая компиляция выключена.
Поле method_name содержит название скомпилированного метода или название метода, содержащего скомпилированный цикл.
Поле size содержит размер скомпилированного байт-кода, не размер полученного машинного кода. Размер указан в байтах.
Поле deopt появляется не в каждом сообщении, оно содержит название проведенной деоптимизации и может содержать такие сообщения как «made not entrant» и «made zombie».
Иногда в логе могут появиться записи вида: timestamp compile_id COMPILE SKIPPED: reason. Они означают, что при компиляции метода что-то пошло не так. Есть случаи, когда это ожидаемо:
Параметр -compiler выводит сводную информацию о работе компилятора (5003 — это ID процесса):
Эта команда также выводит количество методов, компиляция которых завершилась ошибкой и название последнего такого метода.
Планы на вторую часть
В следующей части мы рассмотрим пороговые значения счетчиков при которых JVM запускает компиляцию и как можно их поменять. Мы также рассмотрим как JVM выбирает количество потоков компилятора, как можно его поменять и в каких случаях стоит это делать. И наконец, кратко рассмотрим некоторые из оптимизаций выполняемых JIT компилятором.
JIT-компилятор как учебный проект в Академическом Университете
Около шестнадцати лет назад вышла первая версия Hotspot – реализация JVM, впоследствии ставшая стандартной виртуальной машиной, поставляемой в комплекте JRE от Sun.
Основным отличием этой реализации стал JIT-компилятор, благодаря которому заявления про медленную Джаву во-многих случаях стали совсем несостоятельными.
Сейчас почти все интерпретируемые платформы, такие как CLR, Python, Ruby, Perl, и даже замечательный язык программирования R, обзавелись своими реализациями JIT-трансляторов.
В рамках этой статьи я не планирую проливать свет на малоизвестные детали реализации промышленных JIT-компиляторов, скорее это будет совсем поверхностное ознакомление с азами и рассказ про учебный проект по соответствующей тематике.
Преимущества JIT-компиляции
Чтобы говорить о преимуществах сначала разберемся, с тем, что это такое. Начнем издалека, а именно с определения компилируемого языка.
Компилируемый язык программирования — язык программирования, исходный код которого преобразуется компилятором в машинный код конкретной архитектуры, например x86/ARM. Этот машинный код представляет собой последовательность команд, которая совсем понятна вашему процессору, и может быть исполнена им без посредника.
Интерпретируемые языки программирования отличаются тем, что исходники не преобразовываются в машинный код для непосредственного выполнения на ЦПУ, а исполняются с помощью специальной программы-интерпретатора.
Как промежуточный вариант, многие языки (Java, C#, Python) транслируются в машинно-независимый байт-код,
который все еще не может быть исполнен напрямую на ЦПУ, и чтобы его исполнить все еще необходим интерпретатор.
Почему интерпретация медленней? Ответ на этот вопрос, как ни странно не всем кажется очевидным, хотя он действительно очень простой: интерпретируя каждую команду по отдельности, мы затрачиваем дополнительные ресурсы на перевод семантики этой команды на язык процессора. Кроме того современные процессоры оптимизированы для выполнения последовательных команд (см. Конвейер), а интерпретатор чаще всего представляет собой огромный switch с кучей опций, загоняющий в тупик предсказатель переходов.
Вполне естественным решением всех этих проблем было бы лишь однажды перевести байт-код на язык процессора и передать его ЦПУ для исполнения. Так чаще всего и поступают, называя этот процесс Just-in-time-компиляцией (JIT). Кроме того, именно в течение этой фазы чаще всего проводятся различные оптимизации генерируемого кода.
Теперь перейдем к практике.
Постановка задачи
Курс “Виртуализация и виртуальные машины” в нашем университете читает Николай Иготти, принимавший участие в разработке самых разных классов ВМ: Hotspot, VirtualBox, NativeClient, и не понаслышке знающий детали их реализации. Благодаря чудесам современных технологий, чтобы узнать про это все подробней необязательно даже быть студентом Академического Университета, так как курс опубликован на лекториуме. Хотя нужно отметить, что это конечно немного не то, в силу интерактивности курса и работе с аудиторией на лекциях.
Типичная программа на языке mathvm:
Сложности
Казалось бы, что существенно сложного в том, чтобы просто перевести семантику несложных байт-код инструкций в соответствующие элементы набора инструкций x86?
Asmjit
Самый простой работающий пример использования Asmjit:
Я думаю у людей, знакомых с ассемблером, он не должен вызвать много вопросов. Единственное, о чем следует упомянуть – объект runtime, отвечающий за время жизни выделенной памяти. Она будет освобождена после вызова его деструктора (RAII).
Еще примеры использования можно подсмотреть в каталоге с тестами библиотеки или у меня в репозитории.
Отладка
Оптимизация
После трех дней активного программирования и отладки появился первый совершенно бесхитростный вариант JIT-транслятора, в котором все значения стека и переменных каждый раз бездумно сохранялись в память.
Даже такое решение дало прирост производительности примерно в шесть раз, с 6 FPS, которые получались с интерпретатором до 36 FPS.
Вообще в начале семестра, когда выяснились правила игры, у меня были наполеоновские планы: сделать все совсем по-взрослому – с переводом байткода в SSA и умным алгоритмом регистровой аллокации.
Но в связи с острой нехваткой времени и банальным малодушием все закончилось несколько прозаичней.
Распределение регистров
На всякий случай напомню, что один из наиболее критичных пунктов в плане производительности программы – эффективность использования имеющихся регистров ЦПУ.
Это связано с тем, что основная вычислительная деятельность может производиться только на них, а кроме того чтение/запись даже в память, находящуюся в L1-кэше, работает до двух раз дольше, чем аналогичные операции над регистрами.
Я воспользовался не самым сложным, но зато довольно действенным эвристическим решением: будем хранить в регистрах первые элементы стека виртуальной машины, 7 элементов для слотов общего назначения (строки/целые числа) и 14 для слотов для чисел с плавающей точкой.
Это решение кажется наиболее оправданным, так как наиболее горячими переменными в рамках работы функции действительно является именно низ стека, участвующий во всех вычислениях.
Кроме того, если использовать те же самые регистры, по которым раскладываются аргументы при вызове функций, то это в некоторых случаях позволяет сэкономить время в местах вызова.
В результате реализации этих идей, я получил ускорение на 9 FPS, таким образом достигнув 45 FPS, что не могло меня не радовать.
Peephole-оптимизации
Одним из простых классических подходов при генерации являются так называемые Peephole-оптимизации, идея которых заключается в поиске и замене определенных последовательностей инструкций на другие, более производительные.
Например из-за недостатка выразительности байт-кода mathvm, операторы сравнения вроде (x0
Что такое JIT компиляция в Java?
Каждый язык программирования использует компилятор для преобразования кода языка высокого уровня в двоичный код машинного уровня, поскольку система понимает только двоичный код. В зависимости от типа языка программирования он отличается, в Java также есть свой компилятор.
Понятие JIT компилятора
JIT Compiler в Java – это одна из составных частей Java Runtime Environment. Он в основном отвечает за оптимизацию производительности приложений во время выполнения. В общем, главный девиз компилятора – повышение производительности приложения для конечного пользователя и разработчика приложения.
Нюансы
Принцип работы
JIT или динамический компилятор ускоряет производительность приложений во время выполнения. Поскольку Java программа состоит из классов и объектов. По сути, представляет собой байт-код, который не зависит от платформы и выполняется JVM в различных архитектурах.
На диаграмме ниже показано, как происходит фактическая компиляция в среде выполнения Java.
Когда вы кодируете Java-программу, JRE использует компилятор javac для компиляции исходного кода высокого уровня в байт-код. После этого JVM загружает байт-код во время выполнения и преобразует его в двоичный код машинного уровня для дальнейшего выполнения с использованием Interpreter.
Интерпретация байтового кода Java снижает производительность по сравнению с нативным приложением. Именно здесь JIT-компилятор помогает повысить производительность, компилируя байт-код в машинный код «точно в срок» для запуска.
JIT-компилятор активируется и включается по умолчанию, когда метод вызывается. Когда метод компилируется, виртуальная машина вызывает скомпилированный код метода напрямую, не интерпретируя его. Следовательно, не требует большого использования памяти и процессорного времени. Это в основном ускоряет производительность приложения.
Аспекты безопасности
Компиляция байтового кода в машинный код JIT-компилятором осуществляется непосредственно в памяти. т.е. компилятор передает машинный код непосредственно в память и выполняет его. В этом случае он не сохраняет машинный код на диске перед вызовом файла класса и его выполнением.
По сути, память должна быть помечена как исполняемая. В целях безопасности это должно быть выполнено после записи кода в память. Он также должен быть помечен как доступный только для чтения, поскольку исполняемая память является дырой в безопасности.
Плюсы и минусы JIT в Java
Глубокое погружение В Новый JIT – Компилятор Java- Graal
Изучите функциональные возможности нового компилятора Java JIT – Graal.
1. Обзор
В этом уроке мы более подробно рассмотрим новый компилятор Java Just-In-Time (JIT), называемый Graal.
2. Что такое JIT-Компилятор?
Давайте сначала объясним, что делает JIT-компилятор.
3. Более подробное изучение JIT-компилятора
C1 предназначен для более быстрой работы и создания менее оптимизированного кода, в то время как C2, с другой стороны, занимает немного больше времени, но создает более оптимизированный код. Клиентский компилятор лучше подходит для настольных приложений, так как мы не хотим иметь длительных пауз для JIT-компиляции. Серверный компилятор лучше подходит для длительно работающих серверных приложений, которые могут тратить больше времени на компиляцию.
3.1. Многоуровневая компиляция
Сегодня установка Java использует оба JIT-компилятора во время обычного выполнения программы.
3.2. Серверный Компилятор
Давайте теперь немного сосредоточимся на С2, поскольку он является самым сложным из двух. C2 был чрезвычайно оптимизирован и создает код, который может конкурировать с C++ или быть еще быстрее. Сам серверный компилятор написан на определенном диалекте C++.
Тем не менее, это связано с некоторыми проблемами. Из-за возможных ошибок сегментации в C++ это может привести к сбою виртуальной машины. Кроме того, за последние несколько лет в компиляторе не было реализовано никаких серьезных улучшений. Код в C2 стало трудно поддерживать, поэтому мы не могли ожидать новых серьезных улучшений с текущим дизайном. Имея это в виду, новый JIT-компилятор создается в проекте с именем Graal VM.
4. Проект GraalVM
Проект Graal VM – это исследовательский проект, созданный Oracle. Мы можем рассматривать Graal как несколько связанных проектов: новый JIT-компилятор, основанный на HotSpot, и новая виртуальная машина polyglot. Он предлагает комплексную экосистему, поддерживающую большой набор языков (Java и другие языки на основе JVM; JavaScript, Ruby, Python, R, C/C++ и другие языки на основе LLVM).
Мы, конечно, сосредоточимся на Java.
4.1. Graal – JIT-компилятор, написанный на Java
Graal-это высокопроизводительный JIT-компилятор. Он принимает байт-код JVM и создает машинный код.
Существует несколько ключевых преимуществ написания компилятора на Java. Прежде всего, безопасность, то есть никаких сбоев, кроме исключений, и никаких реальных утечек памяти. Кроме того, у нас будет хорошая поддержка IDE, и мы сможем использовать отладчики, профилировщики или другие удобные инструменты. Кроме того, компилятор может быть независимым от точки доступа, и он сможет создать более быструю JIT-скомпилированную версию самого себя.
4.2. Интерфейс компилятора JVM
JVMCI является частью OpenJDK начиная с JDK 9, поэтому мы можем использовать любой стандартный OpenJDK или Oracle JDK для запуска Graal.
На самом деле JVMCI позволяет нам исключить стандартную многоуровневую компиляцию и подключить наш совершенно новый компилятор (т. Е. Graal) без необходимости что-либо менять в JVM.
Интерфейс довольно прост. Когда Graal компилирует метод, он передает байт-код этого метода в качестве входных данных в JVMCI’. В качестве вывода мы получим скомпилированный машинный код. И вход, и выход-это просто массивы байтов:
В реальных сценариях нам обычно потребуется дополнительная информация, такая как количество локальных переменных, размер стека и информация, собранная в результате профилирования в интерпретаторе, чтобы мы знали, как код выполняется на практике.
По сути, при вызове метода compile () интерфейса JVMCI компилятора |/нам нужно будет передать Запрос на компиляцию объекта. Затем он вернет метод Java, который мы хотим скомпилировать, и в этом методе мы найдем всю необходимую нам информацию.
4.3. Грааль в действии
Сам Graal выполняется виртуальной машиной, поэтому сначала он будет интерпретирован и скомпилирован JIT, когда он станет горячим. Давайте рассмотрим пример, который также можно найти на официальном сайте GraalVM :
Теперь мы скомпилируем его и запустим:
Это приведет к выходу, аналогичному следующему:
Если мы хотим видеть статистику компиляций Graal, нам нужно добавить следующий флаг при выполнении нашей программы:
Это покажет данные, относящиеся к скомпилированному методу, затраченное время, обработанные байт-коды (которые также включают встроенные методы), размер созданного машинного кода и объем памяти, выделенной во время компиляции. Вывод выполнения занимает довольно много места, поэтому мы не будем показывать его здесь.
4.4. Сравнение с компилятором верхнего уровня
Теперь давайте сравним приведенные выше результаты с выполнением той же программы, скомпилированной с помощью компилятора верхнего уровня. Для этого нам нужно сказать виртуальной машине, чтобы она не использовала компилятор JVMCI:
Мы видим, что существует меньшая разница между отдельными временами. Это также приводит к более короткому начальному времени.
4.5. Структура Данных, Лежащая В Основе Graal
Здесь мы видим, что узлы на самом деле не изменились, но у нас есть добавленные ребра потока управления.
4.6. Фактические графики
Давайте рассмотрим простой пример:
Это очень простой поток данных:
На приведенном выше графике мы видим четкое представление нашего метода. Параметры P(0) и P(1) переходят в операцию добавления, которая переходит в операцию деления с константой C(2). Наконец, результат возвращается.
Теперь мы изменим предыдущий пример, чтобы он был применим к массиву чисел:
Мы видим, что добавление цикла привело нас к гораздо более сложному графу:
Что мы можем заметить вот:
4.7. Опережающий режим компилятора
Основная причина, по которой мы будем использовать Graal таким образом, заключается в том, чтобы ускорить время запуска до тех пор, пока обычный многоуровневый подход к компиляции в точке доступа не сможет взять верх.
5. Заключение
В этой статье мы изучили функциональные возможности нового JIT-компилятора Java в рамках проекта Graal.
Сначала мы описали традиционные JIT-компиляторы, а затем обсудили новые функции Graal, особенно новый интерфейс компилятора JVM. Затем мы проиллюстрировали, как работают оба компилятора, и сравнили их производительность.
После этого мы поговорили о структуре данных, которую Graal использует для управления нашей программой, и, наконец, о режиме компилятора AOT как еще одном способе использования Graal.