Спецификация Java 11: Глава 12. Исполнение

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

Виртуальная машина Java запускается, загружая указанный класс или интерфейс, а затем вызывая метод main в этом указанном классе или интерфейсе. В разделе §12.1 описаны шаги загрузки, связывания и инициализации, связанные с выполнением main, в качестве введения в концепции этой главы. Дальнейшие разделы определяют детали загрузки (§12.2), связывания (§12.3) и инициализации (§12.4).

Глава продолжается спецификацией процедур для создания новых экземпляров класса (§12.5); и завершение экземпляров класса (§12.6). Она завершается описанием выгрузки классов (§12.7) и процедуры, выполняемой при выходе из программы (§12.8).

12.1. Запуск виртуальной машины Java

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

Точная семантика запуска виртуальной машины Java дана в главе 5 спецификации виртуальной машины Java, Java SE 11 Edition. Здесь мы представляем обзор процесса с точки зрения языка программирования Java.

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

Например, в реализации UNIX командная строка:

java Test reboot Bob Dot Enzo

обычно запускает виртуальную машину Java, вызывая метод main класса Test (класс в безымянном пакете), передавая ему массив, содержащий четыре строки "reboot", "Bob", "Dot", "Enzo".

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

12.1.1. Загрузка класса Test

Первоначальная попытка выполнить метод main класса Test обнаруживает, что класс Test не загружен, то есть виртуальная машина Java в настоящее время не содержит двоичного представления для этого класса. Затем виртуальная машина Java использует загрузчик классов, чтобы попытаться найти такое двоичное представление. Если этот процесс завершается неудачно, выдается ошибка. Этот процесс загрузки описан далее в §12.2.

12.1.2. Связывание Test: проверка, подготовка, (необязательно) разрешение

После загрузки Test его необходимо инициализировать, прежде чем можно будет вызвать main. И Test, как и все типы (класс или интерфейс), должен быть связан до его инициализации. Связывание включает проверку, подготовку и (необязательно) разрешение. Связывание описано далее в §12.3.

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

Подготовка включает в себя распределение статической памяти и любых структур данных, которые используются внутри реализации виртуальной машины Java, например таблиц методов. Подготовка описана далее в §12.3.2.

Разрешение - это процесс проверки символьных ссылок из Test на другие классы и интерфейсы путем загрузки других упомянутых классов и интерфейсов и проверки правильности ссылок.

Шаг разрешения необязателен во время первоначального связывания. Реализация может разрешать символические ссылки из класса или интерфейса, который связывается очень рано, вплоть до разрешения всех символических ссылок из классов и интерфейсов, на которые далее ссылаются, рекурсивно. (Это разрешение может привести к ошибкам на этих дальнейших этапах загрузки и связывания.) Этот выбор реализации представляет одну крайность и похож на вид "статической" связи, которая использовалась в течение многих лет в простых реализациях языка C. (В этих реализациях скомпилированная программа обычно представлена как файл "a.out", который содержит полностью связанную версию программы, включая полностью разрешенные ссылки на библиотечные процедуры, используемые программой. Копии этих библиотечных процедур включены в файл "a.out".)

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

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

Процесс разрешения описан далее в §12.3.3.

12.1.3. Инициализация Test: выполнение инициализаторов

В нашем продолжающемся примере виртуальная машина Java все еще пытается выполнить метод main класса Test. Это разрешено, только если класс был инициализирован (§12.4.1).

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

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

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

Процесс инициализации описан далее в §12.4.

12.1.4. Вызов Test.main

Наконец, после завершения инициализации класса Test (во время которого могли произойти другие последующие загрузки, связывание и инициализация) вызывается метод main класса Test.

Метод main должен быть объявлен как public static void. Он должен указывать формальный параметр (§8.4.1), объявленным типом которого является массив String. Следовательно, приемлемо любое из следующих заявлений:

public static void main(String[] args)

public static void main(String... args)


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки