В языке Java работа по связыванию не выполняется во время компиляции, а работа по загрузке, связыванию и инициализации типов выполняется во время выполнения виртуальной машины Java. Когда программа Java запускается, виртуальная машина Java запускается с загрузки указанного класса и последующего вызова основного метода класса. В процессе запуска JVM файл байт-кода внешнего класса будет преобразован в данные, выполняемые в JVM посредством ряда процессов. Эта серия процессов называется процессом загрузки класса.
Общий процесс загрузки класса
Весь жизненный цикл включает пять процессов: загрузку, связывание, инициализацию, использование и выгрузку, начиная с момента загрузки класса в память JVM и выгрузки из памяти. Ссылка включает в себя три процесса проверки, подготовки и разбора.
На всем жизненном цикле определяется начальная последовательность пяти процессов загрузки, проверки, подготовки, инициализации и выгрузки, в то время как процесс синтаксического анализа не является обязательным в спецификации Java. эти процессызапускатьзаказ в порядке, новоплощать в жизньПоследовательность не выполняется последовательно, и часть работы чередуется, что будет подробно объяснено в следующих деталях.
время загрузки класса
Спецификация виртуальной машины Java не указывает, когда происходит первый шаг в процессе загрузки классов.нагрузкаЧтобы обеспечить соблюдение ограничений, как определить начальную точку загрузки этого класса? Спецификация виртуальной машины Java принимаетинициализацияЭтапы строго регламентированы, чтобы гарантировать, чтоинициализацияКак процесс, который должен быть запущен заранее, загрузка, проверка и подготовка, конечно, также должны быть запущены до этого.
Положения о виртуальной машине Java,есть и толькоКласс должен быть инициализирован немедленно в следующих пяти случаях:
- Когда виртуальная машина запускается после того, как пользователь указывает основной класс, содержащий основной метод, основной класс должен быть инициализирован первым.
- При создании экземпляра класса с ключевым словом new, чтении или записи статического поля класса или вызове статического метода класса сначала должно быть запущено создание экземпляра класса.
- При использовании отражения для отражающего вызова класса, если класс не инициализирован, его инициализация должна быть запущена первой.
- Инициализируйте класс, и когда родительский класс класса не был инициализирован, вам необходимо сначала инициализировать его родительский класс.
- Используя поддержку динамического языка в версиях после JDK7, результатом анализа экземпляра java.lang.invoke.MethodHandle является дескриптор метода REF_getStatic, REF_putStatic и REF_invokeStatic.Если класс, соответствующий дескриптору, не был инициализирован, его создание экземпляра должно быть инициировано. первый.
Инициализация интерфейса в основном такая же, как и у класса. Единственное отличие состоит в том, что при инициализации интерфейса не требуется, чтобы его родительский интерфейс был инициализирован. Он будет инициализирован только тогда, когда родительский интерфейс фактически используется. .
процесс загрузки класса
Ниже подробно описаны конкретные действия по загрузке, проверке, подготовке, анализу и инициализации в процессе загрузки класса.
нагрузка
Во время фазы загрузки виртуальная машина должна делать три вещи:
- Получите двоичный поток байт-кода класса по его полному имени
- Преобразуйте статическую структуру хранения в этом двоичном потоке байт-кода в структуру данных времени выполнения в области методов.
- Создайте объект java.lang.Class, представляющий класс в памяти в качестве записи доступа к различным данным этого класса в области методов.
Для объекта Class в спецификации виртуальной машины Java не указано, что он хранится в куче Java, а виртуальная машина HotSpot хранит его в области методов.
проверять
В качестве первого шага в процессе связывания проверка примерно завершает следующие четыре этапа действий по проверке:
- проверка формата файла Этот этап в основном отвечает за проверку того, соответствует ли поток байтов спецификации файла класса, когда поток байтов преобразуется в данные времени выполнения в области метода, чтобы гарантировать, что он может быть правильно проанализирован и сохранен в области метода. Следующие проверки основаны на структуре хранения области метода, и поток байтов не будет напрямую манипулироваться.
- проверка метаданных Эта фаза отвечает за анализ того, соответствует ли структура, хранящаяся в области метода, требованиям спецификации языка Java, например, наследует ли класс класс, которому не разрешено наследовать (класс, измененный final), содержит ли он родительский класс и так далее. На этом этапе выполняется проверка типа данных, чтобы убедиться в отсутствии недопустимой информации метаданных.
- Проверка байт-кода Проверка метаданных гарантирует, что тип данных в байт-коде соответствует спецификации языка.Этот этап отвечает за анализ потока данных и потока управления, определение законности тела метода и обеспечение того, что проверенный метод не нанесет вреда виртуальной машине при время выполнения.
- Проверка символьной ссылки Последняя фаза происходит во время фазы синтаксического анализа ссылки. На этапе синтаксического анализа символические ссылки в виртуальной машине преобразуются в прямые ссылки, и этот этап отвечает за сопоставление различных символьных ссылок, чтобы гарантировать существование внешних зависимостей и доступность внешних зависимых классов, полей и методов.
Подготовить
На этапе подготовки формально выделяется память для переменных поля класса и устанавливаются начальные значения. Эти переменные хранятся в области методов, обратите внимание, что переменные здесь Переменные класса (измененные модификатором static), а не переменные экземпляра. Начальные значения типов данных в Java показаны в следующей таблице.
тип данных | Начальное значение |
---|---|
boolean | false |
byte | (byte) 0 |
char | \u0000 |
short | (short) 0 |
int | 0 |
long | 0L |
float | 0F |
double | 0D |
reference | null |
Когда поле класса является константным типом (то есть оно модифицируется static final), поскольку значение поля было определено и не будет изменено в дальнейшем, ему будет непосредственно присвоено указанное значение. Например, значение переменной ниже будет напрямую присвоено 1.
public static final int value = 1;
Разобрать
На этапе синтаксического анализа символические ссылки в пуле констант заменяются прямыми ссылками. В файле байт-кода такие типы, как классы, интерфейсы, поля и методы, представлены набором символов, форма которых определяется форматом файла класса в спецификации виртуальной машины Java. Прежде чем виртуальная машина выполнит конкретную инструкцию, символическая ссылка должна быть преобразована в указатель, относительное смещение или дескриптор цели, чтобы конкретное местоположение вызова можно было найти в памяти с помощью таких прямых ссылок.
инициализация
В файле класса класса есть два специальных метода:
Метод
- Порядок компилятора определяется порядком в исходном файле, и блок статических операторов может обращаться только к переменным, определенным до него, а переменные, определенные после него, могут только присваивать значение, но не могут быть доступны.
public class Test {
static {
// 变量赋值编译可以正常通过
value = 2;
// 访问变量编译失败
System.out.println(i);
}
static int value = 1;
}
- Виртуальная машина гарантирует, что метод
родительского класса был выполнен до того, как будет выполнен метод подкласса. Таким образом, операции в родительском классе видны дочернему классу. - Перед выполнением метода
интерфейса нет необходимости сначала выполнять метод родительского интерфейса.Только при использовании переменных, определенных в родительском интерфейсе, родительский интерфейс будет инициализирован. При этом класс реализации интерфейса не будет выполнять метод родительского интерфейса при инициализации. - Метод
не требуется.Если в классе или интерфейсе нет блоков назначения переменных и статических операторов, компилятор может не сгенерировать метод . - Виртуальная машина обеспечивает правильную блокировку и синхронизацию метода
в нескольких потоках. Если несколько потоков инициализируют класс одновременно, только один поток будет выполнять метод , а другие потоки будут заблокированы.
Суммировать
Правильно освоив процесс загрузки классов, вы сможете глубже разобраться в различных проблемах обычного программирования. Давайте почувствуем важность освоения процесса загрузки класса на примере, который противоречит этому чувству.
class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
private SingleTon() {
count1++;
count2++;
}
public static SingleTon getInstance() {
return singleTon;
}
}
public class Test {
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);
System.out.println("count2=" + singleTon.count2);
}
}
Результат выполнения приведенного выше кода:
count1=1
count2=0
По триггерным условиям инициализации мы знаем, что при вызове метода main getInstance будет выполняться процесс загрузки класса SingleTon.
На этапе подготовки к переменной добавляется начальное значение, в это время singleTon=null, count1=0, count2=0.
На этапе инициализации сначала выполните new SingleTon(), в это время count1=count2=1, а затем count1=1, потому что count1 не имеет операции присваивания. count2 присваивается 0, поэтому результат count1=1, count2=0.
Если **private static SingleTon singleTon = new SingleTon();заявление перемещается вpublic static int count2 = 0, после ** результат count1=1, count2=1.