#java #jvm #jvm-hotspot
#java #jvm #jvm-точка доступа
Вопрос:
Мы знаем, что JVM использует как интерпретатор, так и JIT-компилятор. JIT-компилятор преобразует те байтовые коды, которые повторяются, в машинный код и сохраняет их в памяти. Теперь, когда интерпретатор переводит байт-код построчно и запускает его, он просто пропустит часть перевода для повторяющегося кода, уже преобразованного и сохраненного в памяти, но будет запускать его напрямую. Тем самым уменьшая параллельный избыточный перевод.
Тогда почему Java использует интерпретатор в JVM? Компилятор, подобный JIT, мог бы выполнить всю задачу преобразования байтового кода в машинный код сразу?
Комментарии:
1. Потому что интерпретатор запускает компилятор, когда он принимает решение, и в противном случае выполняет сам код. Не весь код компилируется в машинный код. В противном случае использование памяти было бы бесконечным.
2. Интерпретатор не преобразует. У вас просто неправильное представление о том, что делает интерпретатор.
3. Компилятор, интерпретатор и ассемблер — это трансляторы, которые преобразуют код между компьютерными языками. Они смешиваются: «Простой интерпретатор, написанный на языке низкого уровня, может иметь аналогичные блоки машинного кода, реализующие функции языка высокого уровня, которые хранятся и выполняются, когда запись функции в справочной таблице указывает на этот код. Интерпретатор, написанный на языке высокого уровня, обычно использует другой подход, такой как генерация и последующее прохождение дерева синтаксического анализа, или генерация и выполнение промежуточных инструкций, или и то, и другое. » (Википедия и др.) Поэтому я думаю, что вопрос интересный!
Ответ №1:
В этом ответе есть хорошее сравнение, которое дает мне понять, почему Java использует как AOT, так и JIT.
Упомянутая здесь «тонкая настройка» с помощью JIT выполняется в компиляторе для JVM, который оптимизирован для каждой системы, на которой он работает. И вы не учитываете недостатки, перечисленные в ссылке.
Ответ №2:
Современные реализации Java, такие как GraalVM, фактически предлагают возможность компилировать в машинный код целые классы байт-кода Java — или даже целые приложения — но заблаговременно, а не во время выполнения. Безусловно, можно было бы реализовать JIT-компилятор, который обрабатывал бы весь класс во время выполнения и, таким образом, вообще избегал необходимости в интерпретации.
Но после того, как у вас было время на ужин из трех блюд, пока компилятор собственного кода GraalVM выполняет свою работу, достаточно легко понять, почему массовая предварительная компиляция не является механизмом компиляции по умолчанию в JVM. Компиляция в машинный код может быть медленной, а Java по своей сути не является компилируемым языком.
Большинство JVM предлагают некоторую форму адаптивного баланса интерпретации / компиляции, реализованную во время выполнения. Для разделов кода, которые часто повторяются, интерпретация «медленная», потому что работа по интерпретации повторяется много раз. Для кода, который выполняется только один раз, компиляция «медленная», потому что работа по компиляции должна быть выполнена до фактического выполнения каких-либо программных инструкций.
Таким образом, современные реализации Java предлагают обе стратегии и пытаются сбалансировать их, чтобы получить наилучшую общую производительность во время выполнения. В целом, то, что мы хотели бы сделать, это (JIT) компилировать только те части приложения, где временные затраты на компиляцию в наибольшей степени компенсируются преимуществами, которые она приносит. Как это сделать, было предметом большого исследования, и новые методы продолжают разрабатываться.
Ответ №3:
JRockit JVM не имеет интерпретатора. Он компилирует весь байт-код даже при выполнении отладки, поэтому его не нужно иметь.
Одним из преимуществ интерпретатора является более быстрый запуск. Например, статический инициализатор выполняется только один раз, поэтому обычно нет необходимости его компилировать.
Ответ №4:
Не во всех реализациях JVM используется комбинация интерпретатора и JIT-компилятора. У некоторых есть только интерпретатор, у некоторых только JIT-компилятор или два.
Самая известная JVM HotSpot JVM имеет два интерпретатора, а JIT — два компилятора:
- интерпретатор, написанный на переносимом C . Преимущество в том, что вам нужен только компилятор C в вашей системе для запуска этого интерпретатора
- «шаблонный» интерпретатор, написанный на языке ассемблера. Этот интерпретатор зависит от архитектуры.
- клиентский компилятор: быстрая компиляция, менее эффективный код
- серверный компилятор: более медленная компиляция, высоко оптимизированный код
В зависимости от вашей системы или того, как вы настраиваете HotSpot, он будет содержать либо интерпретатор C , либо шаблонный, эти два не могут быть объединены (afaik).
Здесь у нас есть первое преимущество интерпретации: она более переносима. Существует компилятор C для множества комбинаций аппаратного обеспечения / ОС. Компиляторы JIT создают машинный код и поэтому должны быть перенесены отдельно на каждую поддерживаемую архитектуру.
Еще одним преимуществом является время запуска. Компиляция JIT требует времени, но интерпретатор может просто начать выполнять код мгновенно. Более того, JIT-компилятор может просто работать в фоновом режиме, пока интерпретируется код.
Не только JIT-компилятор может компилироваться во время работы интерпретатора, но он также может генерировать код, который возвращается к интерпретатору. Это означает, что JIT-компилятор не должен поддерживать все функции / угловые случаи (по какой-либо причине). Если он сталкивается с чем-то, что он не может скомпилировать, он может сгенерировать переход к интерпретатору из скомпилированного кода или даже вообще отказаться от компиляции этого кода — это нормально (в некотором смысле), потому что его все еще можно интерпретировать.
Привести убедительные доказательства в пользу какого-либо подхода в таких сложных системах, ну, в общем, сложно. Одной из точек данных может быть то, что другие виртуальные машины, которые считаются передовыми, используют аналогичный подход с интерпретатором и двумя компиляторами: виртуальные машины JavaScript V8 и SpiderMonkey.
OP также спрашивает, почему бы не скомпилировать все заранее. Во-первых, это можно сделать с помощью собственного образа GraalVM, и были некоторые другие подобные технологии. Во-вторых, отказ от компиляции заранее имеет некоторые преимущества: то, что вы можете некоторое время запускать код перед его компиляцией, означает, что вы можете наблюдать, как он ведет себя, и более точно настроить оптимизацию в JIT-компиляторе (ветвь else этого if никогда не выполнялась — не тратьте время на встраиваниебюджет для этого) и поскольку вы можете перейти к интерпретатору, вы также можете выполнять «спекулятивную» оптимизацию (даже не утруждайте компиляцию ветки else), где, если спекуляция завершается неудачей, код возвращается к интерпретатору. Однако эта схема не нуждается в интерпретаторе. Подойдет просто JIT-компилятор, который может переходить от одной компиляции (более оптимизированной) к другой (более универсальной). Это сделал JRockit (afaik).
Ответ №5:
Интерпретатор позволяет запускать и отлаживать код на лету. Компилятор должен быть предварительно скомпилирован для запуска, что означает, что если есть ошибка, которая не улавливается вашей предпочтительной средой, то это потребует от вас возможного исправления и перекомпиляции всего проекта.
Следовательно, он имеет как интерпретатор, так и компилятор, чтобы обеспечить скорость компилятора и при этом позволить разработчикам выполнять отладку на лету, внося небольшие изменения и проверяя исправление, не перекомпилируя каждый раз весь проект.
По сути, в двух словах, основная причина заключается в следующем.
Компилятор — это самый быстрый способ запуска для конечного пользователя
Интерпретатор — это самый быстрый способ отладки и программирования для разработчика
Комментарии:
1. При всем уважении, я не думаю, что это отвечает на вопрос. Я думаю, вопрос заключается в том, почему во время выполнения есть как (JIT) компилятор, так и интерпретатор (но это не я отклонил ответ).
2. Этот ответ даже не имеет смысла. Компилятор, обсуждаемый в первом абзаце, полностью отличается от того, который обсуждается в оставшейся части.
3. Я не понимаю, что вы имеете в виду под этим. Я прочитал вопрос, почему он использует оба при запуске. Его запуск может означать запуск для тестирования во время разработки или запуск для фактического использования в качестве конечного пользователя. Итак, в первом абзаце я рассказал о различиях между интерпретатором и компилятором, затем во втором рассказал, как они работают вместе, и закончил суммированием моих пунктов. Я не верю, что я ошибаюсь, я беру свои знания о том, как работают компилятор и интерпретатор, и объединяю их в наиболее логичных рассуждениях, которые у меня есть