Подтвердить что ты не робот

Выполняется ли проверка байтового кода дважды?

Поэтому я немного запутался относительно проверки байт-кода, который происходит внутри JVM. Согласно книге Deitel и Deitel, Java-программа проходит пять этапов (редактирование, компиляция, загрузка, проверка и выполнение) (глава 1). Верификатор байт-кода проверяет байт-код на этапе проверки. Нигде в книге не упоминается, что верификатор байт-кода является частью загрузчика классов.

Однако согласно docs of oracle , загрузчик классов выполняет задачу по загрузке, связыванию и инициализации, и в процессе связывания он должен проверять байт-код.

Теперь, это проверка байт-кода, о которой говорят Deitel и Deitel, и проверка байт-кода, которая этот документ оракула  говорит о том же процессе?

Или проверка байт-кода происходит дважды, один раз во время процесса связывания, а другой - с помощью верификатора байт-кода?

Изображение, описывающее фазы программы java, упомянутые в книге Дителя и Дителя (я взял этот рисунок из одного из ответов ниже nobalG:)) enter image description here

4b9b3361

Ответ 1

Вы можете понять проверку байтового кода, используя эту диаграмму, подробно описанную в Oracle docs

enter image description here

Вы обнаружите, что проверка байтового кода происходит только один раз не дважды

На иллюстрации показан поток данных и управление с языка Java исходный код через компилятор Java, загрузчик классов и байтовым верификатором и, следовательно, на виртуальную машину Java, содержит систему интерпретатора и времени выполнения. Важным вопросом является что загрузчик классов Java и верификатор байт-кода не делают предположения об основном источнике потока байт-кода - код возможно, пришли из локальной системы или, возможно, прошли полпути вокруг планеты. Верификатор байт-кода действует как своего рода гейткипер: он гарантирует, что код, переданный интерпретатору Java, находится в состоянии соответствия быть выполненным и может работать без страха нарушить Java переводчик. Импортированный код не разрешается каким-либо образом выполнять пока он не пройдет проверку верификатора. Как только верификатор сделано несколько важных свойств:

  • Нет операндов или нижних потоков
  • Типы параметров всех команд байт-кода, как известно, всегда правильные.
  • Доступ к полям объектов, как известно, является законным - закрытым, общедоступным или защищенным.

Пока вся эта проверка кажется мучительно детализированной, к тому времени верификатор байт-кода выполнил свою работу, интерпретатор Java может продолжайте, зная, что код будет работать надежно. Зная эти свойства делают интерпретатор Java намного быстрее, потому что он не должны что-то проверить. Нет проверок типа операнда и нет стека проверки переполнения. Таким образом, интерпретатор может работать на полной скорости без ущерба для надежности.

EDIT: -

Из Oracle Docs Раздел 5.3.2:

Когда метод loadClass загрузчика классов L вызывается с помощью имя N класса или интерфейса C для загрузки, L должен выполнить один из следующие две операции для загрузки C:

  • загрузчик классов L может создать массив байтов, представляющий C как байты структуры ClassFile (§ 4.1); тогда он должен вызывать метод defineClass класса ClassLoader. Вызов defineClass заставляет виртуальную машину Java выводить класс или интерфейс обозначается N, используя L из массива байтов, используя алгоритм найдено в п. 5.3.5.
  • Погрузчик классов L может делегировать загрузку C другому загрузчику классов L '. Это достигается путем передачи аргумента N прямо или косвенно на вызов метода на L ' (обычно это метод loadClass). Результатом вызова является С.

Как правильно прокомментировал Хольгер, пытаясь объяснить это подробнее с помощью пример:

static int factorial(int n)
{
int res;
for (res = 1; n > 0; n--) res = res * n;
return res;
}

Соответствующий байтовый код будет

method static int factorial(int), 2 registers, 2 stack slots
0: iconst_1 // push the integer constant 1
1: istore_1 // store it in register 1 (the res variable)
2: iload_0 // push register 0 (the n parameter)
3: ifle 14 // if negative or null, go to PC 14
6: iload_1 // push register 1 (res)
7: iload_0 // push register 0 (n)
8: imul // multiply the two integers at top of stack
9: istore_1 // pop result and store it in register 1
10: iinc 0, -1 // decrement register 0 (n) by 1
11: goto 2 // go to PC 2
14: iload_1 // load register 1 (res)
15: ireturn // return its value to caller

Обратите внимание, что большинство инструкций в JVM напечатаны.

Теперь вы должны заметить, что правильная работа JVM не гарантируется, если код не удовлетворяет хотя бы следующим условиям:

  • Правильность текста: аргументы инструкции всегда имеют типы, ожидаемые инструкцией.
  • Нет стека над потоком или под потоком: инструкция никогда не выдает аргумент o ff пустой стек, а также толкает результат на полный стек (размер которого равен равный максимальному размеру стека, объявленному для метода).
  • Скрытие кода: счетчик программ всегда должен указывать код для метода, к началу действительной кодировки команд (не опускание конца кода метода, никаких ветвей в середине кодировки команд).
  • Инициализация регистра: загрузка из регистра всегда должна выполняться в менее одного хранилища в этом регистре; другими словами, регистры, которые делают не соответствуют параметрам метода, не инициализируются по методу вход, и это ошибка для загрузки из неинициализированного регистра.
  • Инициализация объекта: когда создается экземпляр класса C, один методов инициализации для класса C (соответствующих конструкторы для этого класса) должны быть вызваны перед классом экземпляр может использоваться.

Цель проверки байтового кода - проверить это условие раз и навсегда, путем статического анализа байтового кода во время загрузки. Байт-код, который проходит проверку, может быть выполнен быстрее.

Также следует отметить, что целью проверки байтового кода является перенос указанной выше вертификации из времени выполнения для загрузки.

Вышеприведенное объяснение было взято из Проверка байт-кода Java: алгоритмы и формализации

Ответ 2

Нет.

Из JVM Spec 4.10:

Несмотря на то, что компилятор для языка программирования Java должен создавать только файлы классов, которые удовлетворяют всем статическим и структурным ограничениям в предыдущих разделах, виртуальная машина Java не гарантирует, что любой файл, который он попросит загрузить, был сгенерирован этим компилятором или правильно сформирован.

И затем далее укажите процесс проверки.

И JVM Spec 5.4.1:

Проверка (§4.10) гарантирует, что двоичное представление класса или интерфейса является структурно правильным (§4.9). Проверка может привести к загрузке дополнительных классов и интерфейсов (§5.3), но не требует их проверки или подготовки.

Раздел, указывающий ссылки на ссылки §4.10 - не как отдельный процесс, а часть загрузки классов.

JVM и JLS - отличные документы, когда у вас есть такой вопрос.

Ответ 3

Нет такой проверки по времени

НЕТ. Что касается проверки, внимательно посмотрите, как программа, написанная в java, проходит через различные этапы следующего изображения, вы увидите, что существует нет таких двух раз проверка, но код проверяется только один раз.

enter image description here

  • EDIT. Программист пишет программу (желательно в блокноте) и сохраняет его как файл .java, который затем используется далее для компиляции, компилятором.
  • COMPILE. Здесь компилятор принимает файл .java, компилирует его и ищет возможные ошибки в объеме программы. Если он обнаруживает какую-либо ошибку, сообщает о них программисту. Если ошибка отсутствует есть, тогда программа преобразуется в байт-код и сохранен как файл .class.

  • LOAD. Теперь основная цель компонента под названием "Loader Class" заключается в загрузке байтового кода в JVM. Он еще не выполняет код, но просто загружает его в память JVM.

  • ПРОВЕРИТЬ. После загрузки кода подпрограмма JVM под названием "Байт" Проверщик кода проверяет байт-код и проверяет его подлинность. Он также проверяет, имеет ли байт-код какой-либо такой код что может привести к некоторым злонамеренным результатам. Этот компонент JVM обеспечивает безопасность.

  • EXECUTE. Следующий компонент - это механизм выполнения. Выполнение двигатель интерпретирует код по строкам, используя Just In Time (JIT) компилятор. Компилятор JIT делает выполнение довольно быстро, но потребляет дополнительную кэш-память.

Ответ 4

В спецификации перечислены 4 фазы при проверке байт-кода. Эти шаги функционально различны, чтобы не ошибиться с повторением того же самого. Подобно тому, как многопроходный компилятор использует каждый проход для настройки для следующего прохода, этапы не повторяются, но организуются для одной общей цели, каждая фаза выполняет определенные задачи.

Если байт-код не изменен, нет причин для его проверки дважды.

Здесь описывается проверка.

http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10

Ответ 5

Проверка кода происходит дважды. Однажды во время компиляции (компиляция завершается с ошибкой, если код имеет недостатки, угрозы) и снова после того, как класс загружается в память во время выполнения (здесь происходит фактическая проверка байтового кода). Да, это происходит вместе с процессом загрузки классов (загрузчиками классов), но сами загрузчики классов могут не выступать в качестве верификаторов. Его JVM (или, скорее, верификатор, присутствующий в JVM), который выполняет проверку.