Это утверждение:
Интерпретатор генерирует и выполняет инструкции машинного кода
Ложь.
Проще говоря, интерпретатор - это программа, которая перебирает инструкции программы (будь то виртуальный или реальный набор команд) и выполняет их одну за другой. Это делается путем программирования того, что должна делать каждая инструкция, и моделирования этого в интерпретаторе.
На самом простом уровне вы можете представить, что интерпретатор выглядит примерно так:
for(byte byteCode : program) {
if(byteCode == ADD_BYTECODE) {
add();
}
// ... others
}
Это не очень отличается от того, как процессор выполняет машинный код, но в случае с ЦП большая часть логики реализуется непосредственно в аппаратном обеспечении.
Полагаю, вы могли бы сказать, что интерпретатор - это программа, которая имитирует процессор в программном обеспечении.
JIT-компилятор выполняет работу по переводу байт-кода в машинный код и его оптимизации на этом пути. Одним из теоретических преимуществ машинного кода перед байт-кодом является, например, то, что конкретный ЦП может иметь специальные инструкции, которые выполняются быстрее, чем эквивалент байт-кода.
В случае JVM это делается, когда метод «горячий», то есть когда он выполняется много. Однако JIT-компиляция занимает много времени (попробуйте запустить Java-программу с флагами -XX:-TieredCompilation -Xcomp
, которая по умолчанию запускает компиляцию C2, вы увидите разницу во времени запуска), поэтому быстрее интерпретировать байт-код в первую очередь. Это также дает возможность собирать данные профилирования, которые представляют собой данные о том, как работает программа (например, сколько раз запускается ветвь if или какие типы используются для динамических вызовов диспетчеризации). Данные профилирования также используются во время JIT-компиляции для лучшей оптимизации.