Java. Является ли двоичный код таким же, как ByteCode?
В Java, означает ли "двоичный код" то же, что и "байт-код Java?"
Это поток в Java?
Файл Java (.java) → [javac] → Файл ByteCode (.class) → [JVM/Java Интерпретатор] → Запуск (сначала преобразование его в двоичный код специфичный для машины)
Спасибо!
Ответы
Ответ 1
Ответ зависит от того, что вы подразумеваете под binary code
.
Java bytecode
- это формат двоичных данных, который включает в себя информацию о загрузке и инструкции для виртуальной машины Java. В этом смысле Java bytecode
является особым видом двоичного кода .
Когда вы используете термин " двоичный код" для обозначения машинных инструкций для реальной архитектуры процессоров (например, IA-32 или Sparc), то это отличается.
Java bytecode
не является двоичным кодом в этом смысле. Это не зависит от процессора.
Ответ 2
JVM - очень сложная программа, и поток там на определенном уровне непредсказуем. Например. поток внутри HotSpot JVM выглядит примерно так:
1) он берет ваш байт-код и интерпретирует его
2) если какой-то метод выполняется довольно часто (некоторое количество раз в течение некоторого промежутка времени), он помечен как "горячий" метод, а JVM планирует его компиляцию на платформу, зависящий от машинного кода (это то, что вы назвали двоичным кодом?). Этот поток выглядит следующим образом:
ByteCode
--> Hige-level Intermediate Representation (HIR)
--> Middle-level Intermediate Representation (MIR)
--> Low-level Intermediate Representation (LIR)
--> Register Allocation
--> EMIT (platform dependent machine code)
Каждый шаг в этом потоке важен и помогает JVM выполнять некоторые оптимизации вашего кода. Разумеется, это не изменяет ваш алгоритм. Оптимизация просто означает, что некоторые последовательности кода могут быть обнаружены и обменены с более эффективным кодом (с тем же результатом). Начиная с этапа LIR, код становится зависимым от платформы (!).
Bytecode может быть полезен для интерпретации, но недостаточно хорош, чтобы легко преобразовать его в собственный код машины. HIR заботится об этом, и его целью является быстрое преобразование байт-кода в промежуточное представление. MIR преобразует все операции в операцию с тремя операндами; ByteCode основан на операции стека:
iload_0
iload_1
iand
который был байт-кодом для простой операции and
, а представление среднего уровня для этого будет выглядеть следующим образом:
and v0 v1 -> v2
LIR зависит от платформы, принимая во внимание наш простой пример с операцией and
и определяя нашу платформу как x86, тогда наш фрагмент кода будет:
x86_and v1 v0 -> v1
x86_move v1 -> v2
потому что операция and
принимает два операнда, первая - назначение, другая - источник, а затем мы возвращаем значение результата в другую "переменную". Следующий этап - это "распределение регистров", потому что платформа x86 (и, вероятно, большинство других) работает с регистрами, а не с переменными (например, промежуточное представление), а также с стеком (например, байт-код). Здесь наш фрагмент кода должен выглядеть следующим образом:
x86_and eax ecx -> eax
и здесь вы можете заметить отсутствие операции "переместить". Наш код содержал только одну строку, и JVM выяснил, что создание новой виртуальной переменной не требуется; мы можем просто повторно использовать регистр eax
. Если код достаточно велик, он имеет много переменных и работает с ними интенсивно (например, используя eax где-то ниже, поэтому мы не можем изменить его значение), тогда вы увидите операцию перемещения слева в машинный код. Это опять о оптимизации:)
Это был поток JIT, но в зависимости от реализации VM может быть еще один шаг - если код был скомпилирован ( "горячий" ) и все еще выполняется много раз, JVM планирует оптимизацию этого кода (например, с помощью inlining).
Хорошо, вывод состоит в том, что путь от байт-кода к машинным кодам довольно интересен, немного непредсказуем и зависит от многих вещей.
btw, описанный выше процесс называется "интерпретация смешанного режима" (когда JVM сначала интерпретирует байт-код, а затем использует компиляцию JIT), примером такой JVM является HotSpot. Некоторые JVM (например, JRockit от Oracle) используют только компиляцию JIT.
Это было очень простое описание того, что происходит там. Я надеюсь, что это помогает понять поток внутри JVM на очень высоком уровне, а также задает вопрос о различиях между байт-кодом и двоичным кодом. Для справок и других вопросов, не упомянутых здесь и связанных с этой темой, пожалуйста, прочитайте аналогичную тему "Почему скомпилированные файлы классов Java меньше C скомпилированных файлов?".
Также не стесняйтесь критиковать этот ответ, назовите меня ошибками или непониманием, я всегда готов улучшить свои знания о JVM:)
Ответ 3
Нет такой вещи, как "машинный независимый байт-код" (это не имеет никакого смысла, если вы об этом подумаете). Bytecode только (для целей этого ответа) используется для таких вещей, как виртуальные машины. Виртуальные машины (такие как JVM) INTERPRET байт-код и использовать некоторую умную и сложную компиляцию "точно в срок" (которая IS машина/зависимая от платформы), чтобы дать вам окончательный продукт.
Итак, в некотором смысле, оба ответа правильные и неправильные. Компилятор Java компилирует код в байт-код Java (независимый от машины). Файлы *.class
, в которых находится байт-код, являются двоичными - они все-таки исполняемы. Виртуальная машина позже интерпретирует эти двоичные файлы *.class
(обратите внимание: при описании файлов как двоичных, это несколько неправильно) и делает различные потрясающие вещи. Чаще всего JVM использует что-то под названием JIT (компиляция "точно в момент времени" ), которая генерирует либо специфичные для платформы, либо машинные инструкции, которые ускоряют различные части выполнения. Однако JIT - это еще одна тема для другого дня.
Edit
Java File (.java) -> [javac.exe] -> ByteCode File (.class) -> [JVM/Java Interpreter] -> Running it(by first converting it into binary code specific to the machine)
Это неверно. JVM ничего не "конвертирует". Он просто интерпретирует байт-код. Единственная часть JVM, которая "преобразует" байт-код, - это когда вызывается JIT-компилятор, который является особым случаем и не должен быть обобщен.
Ответ 4
Оба C/С++ (чтобы взять в качестве примера) и программы Java скомпилированы в Двоичный код. Этот общий термин просто означает, что новый созданный файл не кодирует инструкции по-человечески понятным. (т.е. вы не сможете открыть скомпилированный файл в текстовой программе и прочитать его).
С другой стороны, то, что Binary 0 и 1 кодирует (или представляет), зависит от того, что генерировал компилятор. В случае Java он генерирует команды под названием Bytecode, которые интерпретируются JVM. В других случаях для других языков он может генерировать инструкции IA-32 или SPARC.
В заключение, то, как противопоставлены друг другу термины Двоичный код и Java-байт-код, вводит в заблуждение. Причина заключалась в том, чтобы сделать различие между нормальным двоичным кодом, зависящим от машины, и байт-код Java (также двоичный код), который не является.
Ответ 5
Ответ, который я нашел сегодня для вышеупомянутого вопроса:
Источник: JLS
Загрузка относится к процессу нахождения бинарной формы класса или типа интерфейса с определенным именем, возможно, путем его вычисления "на лету", но более типично, получая двоичное представление, ранее вычисленное из исходного кода компилятором Java, и построение из этой двоичной формы объекта класса для представления класса или интерфейса.
Точная семантика загрузки приведена в главе 5 Спецификации виртуальной машины Java, Java SE 7 Edition. Здесь мы представляем обзор процесса с точки зрения языка программирования Java.
Бинарный формат класса или интерфейса обычно представляет собой формат файла класса, описанный в Спецификации виртуальной машины Java, Java SE 7 Edition, приведенный выше, но возможны другие форматы при условии, что они соответствуют требованиям указанных в §13.1. Метод defineClass класса ClassLoader может использоваться для создания объектов класса из двоичных представлений в формате файла класса.