2013-12-03 4 views
17

Я только что закончил чтение спецификации виртуальной машины Java, и section on class loading оставил меня озадаченным. Насколько я понял, в целом, и после прочтения спецификации, я думал, что общая конкретизации класса состоит из следующих шагов в следующем порядке:Где начинается этап разрешения загрузки класса Java?

  • Создание/Загрузка: Класс loader находит поток байтов, представляющий класс, либо файл, либо сетевой поток, либо все, что реализует загрузчик классов для извлечения. Если ни один класс не найден, выдается ClassNotFoundException. На этом этапе уже происходит некоторая базовая проверка, когда возникает ClassFormatError, если массив байтов не представляет собой класс Java (например, магическое число отсутствует) или UnsupportedClassVersionError, если версия класса не поддерживается работающей JVM пример.

  • Ссылка: Класс подключен к JVM. Если что-то пойдет не так, подкласс LinkageError брошен. Linking состоит из трех подэтапов:

    • Проверка: Это удостоверяется, что поток байт представляет собой класс Java, как, например, что байты-код без формальных ошибок, таких как переполненные операндов стека для байт-коды методы , Если класс не прошел проверку, выдается VerifyError.

    • Подготовка: JVM выделяет память для всех статических полей и может создать шаблон экземпляра для ускорения создания экземпляра. Создаются таблицы виртуальных методов. На этом этапе не будут выбраны конкретные ошибки загрузки классов. (An OutOfMemoryError может быть выброшен, хотя.)

    • Разрешение: Все символические ссылки, которые теперь были загружены в область метода в виде выполнения постоянная бассейна разрешены к реальным типам загруженных этой виртуальной машины Java. Если символическая ссылка может быть разрешена, но приводит к конфликту определений, то вызывается IncompatibleClassChangeError. Если ссылочный класс не найден, вызывается NoClassDefFoundError, который в основном обертывает ClassNotFoundException, который был брошен загрузчиком класса, пытающимся загрузить этот ссылочный класс. Если ссылочный класс ссылается на себя, бросается ClassCircularityError. Разрешение может произойти в одном из двух ароматов, который составляет до реализаторов в JVM

      1. Eager: Все символические ссылки на другие поля, методы и классы будут решены прямо сейчас.

      2. Lazy: Разрешение символических ссылок откладывается до первого использования метода. Это может привести к тому, что класс, ссылающийся на несуществующий класс, никогда не выдает ошибку, если эта ссылка никогда не нуждается в разрешении.

  • Initialization: static Инициализаторы Класс, что определены в классе как Java кода выполняются.Если исключение вызвано таким инициализатором, это исключение перевернуто в ExceptionInInitializerError.

Что меня озадачивает это разрешение фаза выше механизма загрузки класса. Почему разрешение определено как явный шаг в пределах , связывающий, который происходит специально после препарата? Уже в description of the class loading phase упоминается, что

Если C имеет какие-либо прямые суперинтерфейсов, символические ссылки от С до его прямые суперинтерфейсы разрешаются с использованием алгоритма §5.4.3.1.

ли символические ссылки также не решен в то время как проверка происходит, так как проверка described:

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

Я всегда эта картина в виде

Java class loading overview

Источник: http://www.programcreek.com

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

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

Я подозреваю, что это изображение разрешения быть выделенным шагом может быть просто наследство от времени, когда разрешениеникогда не проводились лениво, но имел место, где были решены все остальные символические ссылки.

То, что я хочу знать,: Если разрешениев современных виртуальных машинах следует понимать так, как я описал его?Или я ошибаюсь в этом вопросе, и разрешение все еще может быть понято как выделенный шаг в фиксированной строке времени, как показывает изображение?

+0

Вы считаете, что правы, но определенный ответ может быть получен только при исследовании источников JDK. –

+0

Очень странно. – Mikhail

ответ

5

Ваша фотография показывает разрешение, как всегда появляется после подготовки, но это не сработает. Прямые суперклассы необходимы для подготовки, так как вам нужны знания о полях экземпляров суперклассов для определения макета памяти экземпляра объекта для определенного класса. Кроме того, статические инициализаторы класса и его суперклассы должны были быть выполнены до того, как класс может быть использован, т. Е. Перед созданием экземпляров или перед вызовом статических методов.

Это отличается от разрешения всех других ссылочных типов, которые могут быть отложены дольше. Допускается разрешать тип, используемый в методе, непосредственно перед вызовом метода в первый раз.

Когда вы смотрите в начале главы 5.4.3. Resolution, там указано в явном виде:

Java-инструкции виртуальной машины anewarray, checkcast, GetField, getstatic, InstanceOf, invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, LDC, LDC _w, multianewarray, новый, putfield и putstatic сделать символические ссылки на время выполнения постоянного пула. Выполнение любой из этих инструкций требует разрешения его символической ссылки.

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

+0

Итак, есть два вида на * разрешение *. * Алгоритмическое разрешение *, где постоянные времени выполнения привязаны к символическому значению, что описано в п. 5.4.3, и стадию * разрешения *, которая в настоящее время часто выполняется лениво и поэтому откладывается до тех пор, пока ссылки фактически не используются? –

+1

[§5.4.3] (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.3) описывает разрешение, которое может быть ленивым. Шаги, описанные в [§5.3.5] (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3.5), должны выполняться перед использованием класс. – Holger

0

Трудно сказать, но я думаю, что вы только нашли небольшое расхождение или двусмысленность в документации. Шаги в документации IMHO не определены очень точно, поэтому реализация может быть немного конкретной, шаги могут на самом деле частично перекрываться и т. Д. Основная проблема в реализации - это, вероятно, скорость, а не абсолютная логическая ясность.

Попробуйте изучить исходный код OpenJDK, и вы можете найти что-то интересное.

Смежные вопросы