В каком порядке инициализируются различные части класса, когда класс загружается в JVM? - PullRequest
6 голосов
/ 19 ноября 2009

Представьте себе класс Java, который имеет большинство функций, которые вы можете найти в классе. Например: он наследует от другого класса, реализует несколько интерфейсов, включает в себя некоторые «статические конечные» константы, некоторые конечные константы, некоторые статические переменные, переменные экземпляра, статический блок, блок кода без имени (просто код в {}) конструкторы, методы и т. д.

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

Это восходит к абсолютным основам / внутренностям Java, но я не смог найти хорошую статью, объясняющую правильную последовательность.

Ответы [ 2 ]

4 голосов
/ 19 ноября 2009

Это может быть описано в разделе 2.17.4 JVMS 5.0 / 6

2.17.4 Инициализация

Инициализация класса состоит из:

  • выполняет свои статические инициализаторы (§2.11) и
  • инициализаторы для статических полей (§2.9.2), объявленные в классе.

Инициализация интерфейса состоит из выполнения инициализаторов для полей, объявленных в интерфейсе (§2.13.3.1).

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

Класс T или интерфейс типа T будут инициализированы непосредственно перед тем, как произойдет одно из следующих действий:

  • T является классом, и создается экземпляр T.
  • T является классом и вызывается статический метод T.
  • Используется или присваивается непостоянное статическое поле T. Поле констант - это поле, которое (явно или неявно) является как конечным, так и статическим, и которое инициализируется значением константного выражения во время компиляции. Ссылка на такое поле должна быть преобразована во время компиляции в копию значения константы во время компиляции, поэтому использование такого поля никогда не вызывает инициализацию.

Вызов определенных методов в библиотечных классах (§3.12) также вызывает инициализацию класса или интерфейса. Подробности см. В спецификациях библиотеки классов платформы Java 2 (например, класс Class и пакет java.lang.reflect).

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

Перед инициализацией класса или интерфейса его суперкласс инициализируется, если он не был инициализирован ранее.


Обновленная версия Инициализация в JVMS 8 содержится в главе 5.5

Инициализация класса или интерфейса состоит из выполнения метода инициализации его класса или интерфейса ( §2.9 ).

Класс или интерфейс могут быть инициализированы только в результате:

  • Выполнение любой из инструкций виртуальной машины Java new, getstatic, putstatic или invokestatic, которые ссылаются на класс или интерфейс (§new, §getstatic, §putstatic, §invokestatic).
    Все эти инструкции ссылаются на класс прямо или косвенно через ссылку на поле или ссылку на метод.
    После выполнения новой инструкции указанный класс или интерфейс инициализируется, если он еще не был инициализирован.
    При выполнении инструкции getstatic, putstatic или invokestatic класс или интерфейс, который объявил разрешенное поле или метод, инициализируется, если он еще не был инициализирован.
  • Первый вызов java.lang.invoke.MethodHandle экземпляра, который был результатом разрешения дескриптора метода виртуальной машиной Java ( §5.4.3.5 ) и который имеет вид 2 (REF_getStatic ), 4 (REF_putStatic), 6 (REF_invokeStatic) или 8 (REF_newInvokeSpecial).
  • Вызов определенных рефлексивных методов в библиотеке классов ( §2.12 ), например, в классе Class или в пакете java.lang.reflect.
  • Инициализация одного из его подклассов.
  • Его обозначение как начальный класс при запуске виртуальной машины Java ( §5.2 ).

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

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

Реализация виртуальной машины Java отвечает за обеспечение синхронизации и рекурсивной инициализации с помощью следующей процедуры.
Предполагается, что объект Class уже был проверен и подготовлен и что объект Class содержит состояние, которое указывает на одну из четырех ситуаций:

  • Этот Class объект проверен и подготовлен, но не инициализирован.
  • Этот Class объект инициализируется каким-то конкретным потоком.
  • Этот Class объект полностью инициализирован и готов к использованию.
  • Этот Class объект находится в ошибочном состоянии, возможно, из-за попытки инициализации и неудачи.
1 голос
/ 19 ноября 2009

Как насчет JLS , в частности, раздела 12.4?

...