1. Время загрузки класса
1.1 Концепция механизма загрузки классов виртуальных машин
Виртуальная машина загружает данные, описывающие класс, из файла класса в память, проверяет, преобразует, анализирует и инициализирует данные. Процесс окончательного формирования наиболее прямого типа java, который может использоваться виртуальными машинами, — это механизм загрузки классов виртуальных машин.
1.2 Время загрузки класса
Весь жизненный цикл класса от загрузки в память виртуальной машины до выгрузки памяти включает в себя:
1.3 Время инициализации класса
В спецификации виртуальной машины нет обязательного ограничения на время загрузки, но спецификация строго оговаривает, что есть только пять ситуаций, в которых класс должен быть инициализирован (будут происходить загрузка, проверка и подготовка):
1.3.1 Встречайте новые, getstatic, putstatic, invokestatic При использовании этих четырех инструкций байт-кода, если класс не был инициализирован, его инициализация должна быть запущена первой. Наиболее распространенный сценарий создания этих 4 инструкций: использование новых ключевое слово при создании экземпляра объекта; чтение или установка статического поля класса (которое является окончательным оформление, за исключением случаев, когда компилятор помещает результат в статическое поле пула констант); и когда вызывается статический метод класса.
1.3.2 При использовании метода пакета java.lang.reflect для выполнения вызова отражения к классу, если класс не был инициализирован, его инициализацию необходимо инициировать в первую очередь.
1.3.3 Если при инициализации класса обнаруживается, что его родительский класс не был инициализирован, необходимо сначала инициировать инициализацию своего родительского класса.
1.3.4 При запуске виртуальной машины пользователю необходимо указать основной класс для выполнения (тот, который содержит метод main()), и виртуальная машина сначала инициализирует этот основной класс;
1.3.5 При использовании поддержки динамического языка JDK.7, если окончательный результат разрешения экземпляра java.lang.invoke.MethodHandle REF_getStatic, REF_putStatic, REF_invokeStatic Дескриптор метода и класс, соответствующий этому дескриптору метода, не были инициализированы, вам необходимо сначала инициировать его инициализацию;
Поведение в приведенных выше пяти сценариях называется активной ссылкой на класс. В дополнение к этому все способы ссылки на класс, которые не запускают инициализацию, называются пассивными ссылками. Общие примеры пассивного цитирования включают:
Ссылка на статическое поле родительского класса через подкласс не приводит к инициализации подкласса.
Ссылка на класс через определение массива не вызывает инициализацию этого класса. Этот процесс инициализирует класс массива, который является подклассом, автоматически сгенерированным виртуальной машиной и напрямую унаследованным от Object, который содержит свойства и методы массива.
Константы хранятся в пуле констант вызывающего класса на этапе компиляции, и, по сути, они не связаны напрямую с классом, определяющим константу, поэтому инициализация класса, определяющего константу, не будет инициирована.
2. Процесс загрузки класса
2.1 Загрузка
Загрузка — это этап загрузки класса, будьте осторожны, чтобы не перепутать его.
Процесс загрузки делает три вещи:
2.1.1 Получить поток двоичных байтов, определяющий этот класс по его полному имени.
2.1.2 Преобразуйте статическую структуру хранения, представленную этим потоком байтов, в структуру хранения времени выполнения области метода.
2.1.3 Сгенерируйте объект класса, представляющий этот класс в памяти, как запись доступа к различным данным этого класса в области методов.
Двоичный байтовый поток можно получить следующими способами:
Чтение из ZIP-пакета было обычным явлением и в конечном итоге стало основой для будущих форматов JAR, EAR, WAR.
Полученное из сети, наиболее типичное применение этого сценария — апплет.
Генерация вычислений во время выполнения, этот сценарий является наиболее часто используемой технологией динамического прокси, в java.lang.reflect.Proxy — это двоичный поток байтов прокси-класса, использующий ProxyGenerator.generateProxyClass.
Генерируется другими файлами, типичным сценарием является JSP-приложение, то есть соответствующий класс Class генерируется JSP-файлом.
Чтение из базы данных происходит относительно редко.Например, некоторые серверы промежуточного программного обеспечения (такие как SAP Netweaver) могут выбрать установку программы в базу данных для завершения распределения программного кода по кластерам. ...
2.2 Проверка
Проверка — это первый шаг на этапе подключения. Цель этого этапа — убедиться, что информация, содержащаяся в потоке байтов файла Class, соответствует требованиям текущей виртуальной машины и не поставит под угрозу безопасность самой виртуальной машины.
Если виртуальная машина не проверяет входящий поток байтов и полностью ему не доверяет, это может привести к сбою системы из-за загрузки вредоносного потока байтов, поэтому проверка является важной задачей для виртуальной машины, чтобы защитить себя. Является ли этот этап строгим или нет, напрямую определяет, сможет ли виртуальная машина Java противостоять атакам вредоносного кода.
В целом этап проверки ориентировочно завершит четыре этапа проверочных работ:Форматы файлов, метаданные, байт-код, ссылки на символы.
2.2.1 Проверка формата файла
Убедитесь, что поток байтов соответствует спецификации формата файла класса и может быть обработан текущей версией виртуальной машины.
2.2.2 Проверка метаданных
Для информации, описанной байт-кодом, выполняется семантический анализ, чтобы гарантировать, что описанная информация соответствует требованиям спецификации языка Java.
2.2.3 Проверка байт-кода
Убедитесь, что семантика программы является законной и логичной с помощью анализа потока данных и потока управления.
2.2.4 Символические ссылки
Происходит, когда виртуальная машина преобразует символическую ссылку в прямую ссылку и выполняет проверку соответствия информации, отличной от самого класса (различные символические ссылки в пуле констант).
3 Подготовка
Фаза подготовки — это фаза формального выделения памяти для переменных класса (статических переменных-членов) и установки начального значения (нулевое значение) переменных класса.Память, используемая этими переменными, будет выделена в области методов.В настоящее время для выделения памяти выделяются только переменные класса, а не переменные экземпляра.Переменные экземпляра будут выделены в куче вместе с объектом при создании экземпляра объекта. Во-вторых, упомянутое здесь начальное значение «обычно» является нулевым значением типа данных, при условии, что переменная класса определена как:
public static int value=123;
Тогда значение переменной value после этапа подготовки равно 0 вместо 123. Поскольку ни один java-метод в это время не выполнялся, а инструкция putstatic, присваивающая значение 123, сохраняется в методе конструктора класса () после компиляции программы, поэтому действие по присвоению значения 123 будет выполняться в фаза инициализации. Что касается «особого случая», это означает, что когда свойство field поля класса равно ConstantValue, оно будет инициализировано указанным значением на этапе подготовки, поэтому после того, как оно помечено как окончательное, значение value инициализируется до 123 вместо 0 на этапе подготовки.
public static final intvalue =123;
4 Анализ
Фаза разрешения — это процесс, посредством которого виртуальная машина заменяет символические ссылки в пуле констант прямыми ссылками. Действие синтаксического анализа в основном выполняется для 7 типов символических ссылок, а именно класса или интерфейса, поля, метода класса, метода интерфейса, типа метода, дескриптора метода и квалификатора сайта вызова.
5 Инициализировать
Фаза инициализации действительно начинает выполнять программный код Java, определенный в классе. Фаза инициализации — это процесс, в котором виртуальная машина выполняет метод конструктора класса ().
На этапе подготовки переменной класса присваивается требуемое системой начальное значение, а на этапе инициализации переменная класса и другие ресурсы инициализируются в соответствии с субъективным планом, составленным программистом через программу.
Метод
Он генерируется компилятором, автоматически собирает все присвоения переменных класса в классе и объединяет операторы в блок статических операторов (блок static {}). Порядок, в котором компилятор собирает, определяется порядком, в котором появляются операторы в исходном файле. Важно отметить, что блок статических операторов может получить доступ только к переменным класса, определенным до него, а переменные класса, определенные после него, могут быть только назначены, и к ним нельзя получить доступ. Например следующий код:
public classTest{
static{
i =0;// Присвоение переменной переменной может быть скомпилировано обычным образом через System.out.print(i);// Компилятор выдаст сообщение "Недопустимая прямая ссылка"
}
static int i =1;
}
с конструктором класса (или конструктором экземпляра
Поскольку метод
static class Parent {
public static intA =1;
static{
A =2;
}
}
static class Sub extends Parent {
public static int B = A;
}
public static void main (String[] args){
System.out.println(Sub.B);// Результатом вывода является значение статической переменной A в родительском классе, равное 2.
}
Метод
Блоки статических операторов нельзя использовать в интерфейсах, но по-прежнему существуют операции присваивания для инициализации переменных класса, поэтому интерфейсы и классы будут генерировать
Виртуальная машина гарантирует
6 тематических исследований
Из-за последовательного выполнения сначала создается одноэлементный объект, затем в куче открывается пространство и выполняется обычный блок кода, затем выполняется метод конструктора [init], а затем счетчикам count1 и count2 присваивается значение 1. ; затем последовательность Выполняется операция инициализации, после чего count1 присваивается значение 1, count2 равно 5, и результат следующий.
Здесь сначала присваивается значение статической переменной, count1 равно 0, count2 равно 5, затем создается объект singleton, затем создается пространство в куче и выполняется общий блок кода, а затем метод конструктора [init] выполняется, и count1 равен +1 равен 1; count2 также увеличивается на 1; наконец, выполняется статический блок кода, и результат выглядит следующим образом
7 загрузчиков классов
Команда разработчиков виртуальной машины поместила действие «получения двоичного потока байтов (т. е. байт-кода), описывающего этот класс, через полное имя класса» на этапе загрузки класса за пределы виртуальной машины Java, чтобы приложение могло Решайте сами, как получить нужные вам классы. Модуль кода, реализующий это действие, называется «загрузчиком классов».
7.1 Классы и загрузчики классов
Для любого класса он должен быть установлен загрузчиком классов, который его загружает, и самим классом. Уникальный для виртуальной машины Java, каждый загрузчик классов имеет независимое пространство имен классов. С точки зрения непрофессионала: сравните, являются ли два класса «равными» (здесь «равные», включая Возвращаемый результат метода equals(), метода isAssignableFrom() и метода isInstance() объекта класса, включая использование ключевое слово instanceof() используется для определения принадлежности объекта и т. д.), это имеет смысл только в том случае, если два класса загружаются одним и тем же загрузчиком классов, в противном случае, даже если два класса происходят из одного и того же Файлы классов загружаются одной и той же виртуальной машиной. Пока загрузчики классов, которые их загружают, различны, два класса не должны быть равными.
7.2 Классификация загрузчиков класса
С точки зрения виртуальной машины Java существует только два разных загрузчика классов:
Одним из них является загрузчик классов начальной загрузки (Bootstrap ClassLoader), который реализован на C++ и является частью самой виртуальной машины, а другой является загрузчиком для всех остальных классов, которые Реализация, независимая от внешней виртуальной машины, и все наследуется от абстрактного класса java.lang.ClassLoader.
С точки зрения Java-разработчика, загрузчики классов можно разбить на более мелкие части:
Загрузчик классов Bootstrap (Bootstrap ClassLoader) Этот класс загрузчика отвечает за размещение файлов, хранящихся в каталоге \lib, или по -Xbootclasspath В пути, указанном параметром, и он распознается виртуальной машиной (распознается только по имени файла, например rt.jar, библиотека классов, имя которой не совпадает, даже помещается в lib каталог не будет загружен) библиотека классов загружается в память виртуальной машины. Загрузчик класса запуска не может использоваться Java На программу ссылаются напрямую.Когда пользователи пишут собственный загрузчик классов, если им нужно делегировать запрос на загрузку загрузчику классов запуска, они могут вместо этого напрямую использовать null.
Загрузчик класса расширения ClassLoader) Этот загрузчик классов создан Реализован ExtClassLoader (sun.misc.Launcher$ExtClassLoader). Он отвечает за преобразование /lib/ext или Все библиотеки классов в пути, указанном системной переменной java.ext.dir, загружаются в память, и разработчики могут напрямую использовать загрузчик классов расширения.
Загрузчик класса приложения (приложение ClassLoader) Этот загрузчик классов создан Реализуется AppClassLoader (sun.misc.Launcher$AppClassLoader). Поскольку этот загрузчик классов getSystemClassLoader() в ClassLoader Поэтому возвращаемое значение метода обычно называют загрузчиком системного класса. Он отвечает за загрузку библиотеки классов, указанной в пути к классам пользователя (ClassPath). Разработчики могут использовать этот загрузчик классов напрямую. Если приложение не настроило свой собственный загрузчик классов, в общем, это класс по умолчанию в программе. Загрузчик.
7.3 Модель родительского делегирования
Приложения загружаются тремя загрузчиками классов в сотрудничестве друг с другом.При необходимости вы также можете добавить свои собственные определенные загрузчики классов. Иерархическая связь между загрузчиками классов, показанная на следующем рисунке, называется моделью родительского делегирования загрузчиков классов (Parents Делегация Модель). Эта модель требует, чтобы все загрузчики классов, кроме загрузчика запускаемого класса верхнего уровня, имели свой собственный загрузчик родительского класса.Здесь отношения родитель-потомок между загрузчиками классов обычно реализуются через отношение композиции, а не через наследование.(Наследование) реляционная реализация .
7.3.1 Рабочий процесс
Если загрузчик класса получает запрос на загрузку класса, он не пытается загрузить его первым, а рекурсивно делегирует запрос загрузчику родительского класса, что имеет место для каждого уровня загрузчиков. Таким образом, все запросы на загрузку должны в конечном итоге направляться загрузчику классов запуска верхнего уровня, и только когда родительский загрузчик сообщает, что не может выполнить запрос на загрузку (не находит требуемый класс в своей области поиска), дочерний загрузчик попробую сам загрузить.
7.3.2 Преимущества
Используйте модель родительского делегирования для организации отношений между загрузчиками классов таким образом, чтобы Класс Java имеет приоритетную иерархию вместе со своим загрузчиком классов. Например, класс java.lang.Object, который хранится в rt.jar. , независимо от того, какой загрузчик классов хочет загрузить этот класс, он в конечном итоге делегируется для загрузки загрузчику запускаемого класса в верхней части модели, поэтому Object Класс — это один и тот же класс в различных средах загрузчика классов программы. И наоборот, если нет родительской модели делегирования, каждый загрузчик классов загружает себя, если пользователь пишет класс с именем java.lang.Object. class и поместите его в ClassPath программы, тогда в системе будет несколько разных классов объектов, и программа превратится в беспорядок. Если разработчик пытается написать Классы Java с тем же именем в библиотеке классов rt.jar обнаружат, что они могут быть скомпилированы нормально, но никогда не будут загружены и запущены.
7.4 Нарушение модели родительского делегирования
Модель родительского делегирования в основном трижды «уничтожалась» в больших масштабах.
Первая ошибка связана с тем, что загрузчик классов и абстрактный класс java.lang.ClassLoader существовали в JDK 1.0, а модель родительского делегирования была введена после JDK 1.2.Чтобы быть совместимым с существующим определяемым пользователем загрузчиком классов, A При введении модели родительского делегирования был сделан определенный компромисс: в java.lang.ClassLoader был введен метод findClass().До этого единственной целью для пользователей наследовать java.lang.Classloader было переопределение метода loadClass(). После JDK1.2 пользователям не рекомендуется переопределять метод loadClass(), а нужно писать собственную логику загрузки класса в методе findClass().Если родительский класс не загружается в методе loadClass(), он вызывает свой собственный findClass() для завершения загрузки, чтобы убедиться, что вновь написанный загрузчик классов соответствует правилам родительской модели делегирования.
второе разрушение Это происходит из-за дефектов самой модели, таких как: служба JNDI ожидает загрузки загрузчика класса запуска, но загрузчик класса запуска не распознает его. Чтобы решить эту проблему, группа разработчиков Java представила загрузчик классов контекста потока во время разработки (Thread Context ClassLoader)». Это позволяет загрузчику родительского класса запрашивать загрузчик дочернего класса для завершения действия по загрузке класса. Но это нарушает общие принципы модели родительского делегирования.
Третий разрыв вызван стремлением пользователя к динамизму программы. Упомянутый здесь динамизм относится к популярным словам, таким как «горячая замена кода», «горячее развертывание модуля» и так далее. Грубо говоря, я надеюсь, что приложение можно будет использовать сразу же, как нашу компьютерную периферию, подключив мышь и U-диск без перезагрузки машины.