Файл класса
Код Java, который мы пишем, после компиляции компилятором становится.class
файл, из собственного машинного кода в байт-код. Класс-файл представляет собой набор бинарных потоков на основе 8-битных байтов.Каждый элемент данных компактно расположен в класс-файле в строгом соответствии с порядком, без добавления какого-либо разделителя посередине, что делает почти весь контент, хранящийся в весь файл класса. Это необходимые данные для запуска программы, и нет никакого пробела. В Class файле всего 2 структуры данных: беззнаковые числа и таблицы.
Первые 4 байта каждого файла класса называются Magic Number, а значение равно0xCAFEBABE
. Следующие 4 байта — это номер версии файла класса. После этого это конкретная информация о классе, такая как постоянный пул, индекс класса, индекс родительского класса, индекс интерфейса, поля, методы и другая информация.
Так называемая загрузка класса заключается в чтении файла класса в память.
жизненный цикл класса
Весь жизненный цикл класса начинается с загрузки в память виртуальной машины и выгрузки памяти.Весь его жизненный цикл включает: Загрузка, Проверка, Подготовка, Разрешение, Инициализация и использование.(Использование) и выгрузка(Выгрузка) 7 этапов . Три части проверки, подготовки и анализа в совокупности называются «связыванием».Определяется порядок 5 фаз загрузки, проверки, подготовки, инициализации и выгрузки, и процесс загрузки класса должен начинаться пошагово в этом порядке, при этом фаза синтаксического анализа не обязательна: в некоторых случаях она может быть после инициализации фаза Опять же, это для поддержки связывания во время выполнения (также известного как динамическое или позднее связывание) языка Java. Обратите внимание, что пошаговое «начало», а не пошаговое «идти» или «окончание» подчеркивает это, потому что эти фазы обычно переплетаются и смешиваются, обычно вызывая, во время выполнения фазы Активируйте другую стадию.
нагрузка
На этапе загрузки виртуальная машина делает 3 вещи:
- Получите двоичный поток байтов, определяющий этот класс по его полному имени.
- Преобразуйте статическую структуру хранения, представленную этим потоком байтов, в структуру данных времени выполнения области метода.
- Генерирует представление этого класса в памяти
java.lang.Class
Объект, как запись доступа к различным данным этого класса в области методов.
проверять
Проверка — это первый шаг на этапе подключения. Цель этого этапа — убедиться, что информация, содержащаяся в потоке байтов файла Class, соответствует требованиям текущей виртуальной машины и не поставит под угрозу безопасность самой виртуальной машины.
На этапе проверки будут завершены четыре этапа инспекционных действий.
- Проверка формата файла: убедитесь, что поток байтов соответствует спецификации формата файла класса и может быть обработан текущей версией виртуальной машины. например, магическое число
0xCAFEBABE
В начале следует определить, могут ли текущая виртуальная машина обрабатывать основной и дополнительный номера версий, тип константы, соответствует ли индекс, указывающий на константу, требованиям и т. д. Проверка на этом этапе основана на бинарном потоке байтов.Только после прохождения проверки на этом этапе поток байтов попадет в область метода памяти для хранения, поэтому следующие три этапа проверки все основаны на методе Структура выполняется, и поток байтов больше не будет напрямую манипулироваться. - Проверка метаданных: выполняется семантический анализ информации, описанной байт-кодом, чтобы гарантировать, что описанная информация соответствует требованиям спецификации языка Java. например наследование.
- Проверка байт-кода: проверьте и проанализируйте тело метода класса, чтобы убедиться, что метод проверенного класса не вызовет событий, угрожающих безопасности виртуальной машины во время выполнения. Посредством анализа потока данных и потока управления семантика программы определяется как законная и логичная.
- Проверка символической ссылки: проверьте соответствие информации, отличной от самого класса (различные символические ссылки в пуле констант), чтобы убедиться, что действие синтаксического анализа может выполняться нормально. Это происходит, когда виртуальная машина преобразует символическую ссылку в прямую ссылку.Это действие преобразования будет происходить на третьем этапе связывания — этапе синтаксического анализа.
Этап проверки очень важен, но не обязателен. Это не влияет на время выполнения программы, если ссылочный класс был неоднократно проверен, его можно считать-Xverify:none
параметр, чтобы отключить большинство мер проверки класса, чтобы сократить время загрузки класса виртуальной машины.
Подготовить
Фаза подготовки — это фаза формального выделения памяти для переменных класса и установки начальных значений переменных класса, память, используемая этими переменными, будет выделена в области методов. На этом этапе необходимо подчеркнуть две запутанные концепции. Во-первых, в настоящее время для выделения памяти выделяются только переменные класса (переменные, измененные статическими), а не переменные экземпляра. Переменные экземпляра будут выделяться при создании экземпляра объекта. , Вместе с объектами выделяются в куче Java. Во-вторых, упомянутое здесь начальное значение "обычно" является нулевым значением типа данных.
Предположим, переменная класса определена как: public static int value = 123;
Начальное значение переменной value после этапа подготовки равно 0 вместо 123, поскольку выполнение Java еще не началось. метод, а инструкция putstatic, которая присваивает значение 123, сохраняется в конструкторе класса
() после компиляции программы. Таким образом, в методе действие присвоения значения 123 не будет выполняться до фазы инициализации.
Конечно, есть особые случаи: если свойство ConstantValue существует в таблице свойств поля поля класса, значение переменной будет инициализировано значением, указанным свойством ConstantValue на этапе подготовки.
Предположим, что определение приведенного выше значения переменной класса становится следующим: public static final int value = 123;
При компиляции Javac сгенерирует свойство ConstantValue для значения.На этапе подготовки виртуальная машина присвоит значение 123 в соответствии с настройкой ConstantValue.
Разобрать
Фаза разрешения — это процесс, посредством которого виртуальная машина заменяет символические ссылки в пуле констант прямыми ссылками. Действие синтаксического анализа в основном выполняется для 7 типов символических ссылок, а именно класса или интерфейса, поля, метода класса, метода интерфейса, типа метода, дескриптора метода и квалификатора сайта вызова.
- Символические ссылки: символьные ссылки используют набор символов для описания цели, на которую ссылаются. Является литералом любой формы, если его можно использовать для определения цели без двусмысленности. Символические ссылки и реализации виртуальных машин Независимо от схемы памяти, цель, на которую ссылаются, не обязательно должна быть загружена в память. Схема памяти различных реализаций виртуальных машин может различаться. не одно и то же, но символические ссылки, которые они могут принимать, должны быть согласованы, потому что буквальная форма символических ссылок четко определена. В формате файла класса спецификации виртуальной машины Java.
- Прямые ссылки: Прямые ссылки могут быть указателями непосредственно на цель, относительными смещениями или Ручка, которая может быть расположена косвенно на цель. Прямые ссылки связаны со структурой памяти, реализованной виртуальной машиной, и те же символические ссылки Прямые ссылки, переведенные для использования в разных экземплярах виртуальных машин, обычно не будут одинаковыми. Если имеется прямая ссылка, цель ссылки Цель должна уже существовать в памяти.
инициализация
Этот шаг запускает выполнение программного кода Java (или байт-кода), определенного в классе. Виртуальная машина обеспечивает правильную блокировку и синхронизацию метода инициализации класса в многопоточной среде.Если несколько потоков инициализируют класс одновременно, только один поток будет выполнять метод инициализации этого класса, а другим потокам потребуется для блокировки. Подождите, пока активный поток завершит выполнение.
Этапы инициализации JVM
- Если класс не загружен и не связан, программа сначала загрузит и свяжет класс
- Если непосредственный родительский класс этого класса не был инициализирован, то он инициализируется непосредственно родительским классом.
- Если в классе есть операторы инициализации, система выполняет эти операторы инициализации по очереди.
время инициализации класса
Инициализация класса запускается только тогда, когда класс активно используется.Активное использование класса включает следующие шесть типов:
- Создать экземпляр класса, то есть по-новому
- Доступ к статической переменной класса или интерфейса или присвоение значения статической переменной
- вызов статического метода класса
- отражение, например
Class.forName("com.mysql.jdbc.Driver")
- Когда инициализируется подкласс класса, его родительский класс также будет инициализирован.
- Класс, отмеченный как класс запуска (тест Java), когда запускается виртуальная машина Java, напрямую использует команду java.exe для запуска основного класса.
загрузчик классов
Команда разработчиков виртуальной машины поместила действие «получение двоичного потока байтов, описывающего этот класс, через полное имя класса» на этапе загрузки класса за пределы виртуальной машины Java, чтобы приложение могло решить, как получить все данные требуемого класса. Модуль кода, реализующий это действие, называется «загрузчиком классов».
Модель родительского делегирования
С точки зрения виртуальной машины Java существует только два разных загрузчика классов: один — Bootstrap ClassLoader, который реализован на C++ и является частью самой виртуальной машины, а другой — Bootstrap ClassLoader. , которые реализованы языком Java, независимо от внешней среды виртуальной машины, и все они наследуются от абстрактного класса java.lang.ClassLoader.
С точки зрения Java-разработчика, загрузчики классов можно разделить на следующие 3 типа:
- Bootstrap ClassLoader: отвечает за загрузку файлов, хранящихся в каталоге JAVA_HOME\lib, или
-Xbootclasspath
Библиотека классов в пути, указанном параметром и распознаваемом виртуальной машиной (например, rt.jar, все классы, начинающиеся с java., загружаются Bootstrap ClassLoader). Программа Java не может напрямую ссылаться на загрузчик класса запуска. - Extension ClassLoader: этот загрузчик состоит из
sun.misc.Launcher$ExtClassLoader
реализация, отвечающая за загрузку каталога JAVA_HOME\lib\ext, илиjava.ext.dirs
Для всех библиотек классов в пути, указанном системной переменной, разработчики могут напрямую использовать загрузчик классов расширения. - Application ClassLoader (Application ClassLoader): этот загрузчик классов состоит из
sun.misc.Launcher$AppClassLoader
Он отвечает за загрузку класса, указанного пользователем в пути к классам (ClassPath). Разработчики могут использовать загрузчик классов напрямую. Если приложение не настроило свой собственный загрузчик классов, в общем, это загрузчик классов по умолчанию в программе.
Наши приложения загружаются этими загрузчиками 3 классов в сотрудничестве друг с другом, при необходимости вы также можете добавить собственный определенный загрузчик классов.
Модель родительского делегирования требует, чтобы все загрузчики классов, кроме загрузчика классов запуска верхнего уровня, имели свои собственные загрузчики родительских классов. Родительско-дочерние отношения между загрузчиками классов здесь обычно не заканчиваются继承
отношения для достижения, но оба используют组合
отношения для повторного использования кода родительского загрузчика. Это не обязательная модель ограничений, а реализация загрузчика классов, рекомендованная разработчиками Java.
Рабочий процесс модели родительского делегирования: если загрузчик класса получает запрос на загрузку класса, он не будет пытаться загрузить класс сам по себе, а делегирует запрос загрузчику родительского класса для завершения, и каждый уровень загрузки класса Это верно для всех загрузчиков, поэтому все запросы на загрузку в итоге должны передаваться загрузчику класса запуска верхнего уровня, только когда родительский загрузчик сообщает, что не может выполнить запрос на загрузку (необходимый класс не найден в его области поиска), подзагрузчик попытается загрузить его самостоятельно.
Анализ исходного кода ClassLoader:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 首先判断该类型是否已经被加载
Class c = findLoadedClass(name);
if (c == null) {
//如果没有被加载,就委托给父类加载或者委派给启动类加载器加载
try {
if (parent != null) {
//如果存在父类加载器,就委派给父类加载器加载
c = parent.loadClass(name, false);
} else {
//如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name)
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
Анализируя исходный код, мы знаем, что модель родительского делегирования может гарантировать, что каждый класс будет загружен только один раз (аналогично механизму кэширования).
Ссылаться на
- «Понимание второго издания виртуальной машины Java»
- чистая улыбка