Задавать вопросы! JVM类加载你真的【了解】了吗?掌握这几点,不再难!

задняя часть JVM
Задавать вопросы! JVM类加载你真的【了解】了吗?掌握这几点,不再难!

Эта статья участвовала в "Проект «Звезда раскопок»”, чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.

Во-вторых, подсистема загрузки классов

缩略图

2.1, загрузчик классов и процесс загрузки классов

2.1.1, подсистема загрузки классов

类加载子系统

Подсистема загрузчика классов отвечает за загрузку файлов классов из файловой системы или сети.Файл класса имеет определенный идентификатор файла в начале файла.Загрузчик классов отвечает только за загрузку файла класса.Что касается того, может ли он работать, это определяется Execution Engine (движок выполнения).

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

2.1.2, загрузчик классов ClasLoader

类加载器

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

Файл класса загружается в JVM, называется шаблоном метаданных ДНК и помещается в область методов.

Файл .class -> JVM-> в конечном итоге становится шаблоном метаданных.Для этого процесса требуется транспортный инструмент (загрузчик классов), который играет роль курьера.

2.1.3 Этап загрузки

加载阶段

На этапе загрузки он в основном проходит три этапа: загрузка, связывание (проверка, подготовка, синтаксический анализ) и инициализация.

2.1.3.1 Загрузка

Этап загрузки делится на три части:

  1. Получите поток двоичных байтов, определяющий этот класс, через полное имя класса и загрузите файл класса с жесткого диска в память JVM.
  2. Преобразуйте статическую структуру хранения, представленную этим потоком байтов, в структуру данных времени выполнения области метода. (JDK8 раньше назывался постоянным поколением, JKD8 и позже назывался метапространством).
  3. Генерирует представление этого класса в памятиjava.lang.ClassОбъект, как запись доступа к различным данным этого класса в области методов

Существует семь способов загрузки файлов классов:

  1. Загрузка напрямую из локальной системы

  2. Получено через сеть, типичный сценарий: веб-апплет

  3. Прочитайте из zip-архива и станьте основой для формата jar и war в будущем.

  4. Генерация расчетов во время выполнения, наиболее используемая из них: технология динамического прокси.

  5. Генерируется другими файлами, типичный сценарий: JSP-приложение

  6. Извлечение файлов .class из проприетарных баз данных, которые встречаются относительно редко.

  7. Получено из зашифрованных файлов, типичные меры защиты от декомпиляции файлов класса

2.1.3.2. Этап связи

2.1.3.2.1 Проверка

Цель проверки — убедиться, что информация, содержащаяся в потоке байтов файла Class, соответствует требованиям текущей виртуальной машины, обеспечивает корректность загруженного класса и не угрожает безопасности самой виртуальной машины.

В основном он включает четыре вида проверки, проверку формата файла, проверку метаданных, проверку байт-кода и проверку ссылки на символ.

2.1.3.2.2. Подготовить

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

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

2.1.3.2.3, Решить

Этап синтаксического анализа заключается в преобразовании пула константПроцесс преобразования символических ссылок в прямые ссылки(с адресом памяти объекта, а не с самим объектом). На самом деле операции синтаксического анализа часто выполняются после того, как JVM выполнила инициализацию.

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

Действия синтаксического анализа в основном относятся к классам или интерфейсам, полям, методам классов, методам интерфейса, типам методов и т. д. Соответствует CONSTANT_Class_info, CONSTANT_Fieldref_info, CONSTANT_Methodref_info и т. д. в пуле констант.

2.1.3.3 Фаза инициализации

Этап инициализации заключается в выполнении метода конструктора класса.<clinit>()процесс. Этот метод не нуждается в определении.Это комбинация действий присваивания всех переменных класса в классе и операторов в статическом блоке кода, которые автоматически собираются компилятором javac.

Инструкции в методе-конструкторе выполняются в том порядке, в котором операторы появляются в исходном файле.<clinit>()В отличие от конструкторов классов. (Ассоциация: Конструктор с точки зрения виртуальной машины<init>())

Если у класса есть родительский класс, JVM гарантирует, что подкласс<clinit>()Перед выполнением родительский класс<clinit>()был выполнен, и виртуальная машина должна гарантировать, что класс<clinit>()Методы синхронно заблокированы при многопоточности, что означает, что класс не будет инициализирован дважды.

2.1.4, процесс загрузки класса

/**
 *示例代码
 */
public class HelloLoader {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

类加载的过程

2.2, классификация погрузчиков

JVM поддерживает два типа загрузчиков классов. Они есть:

  1. Bootstrap ClassLoader.
  2. Пользовательский ClassLoader. (Расширенный загрузчик классов, загрузчик системных классов, пользовательский загрузчик классов)

Концептуально пользовательский загрузчик классов обычно относится к загрузчику классов, настроенному разработчиком в программе, но спецификация виртуальной машины Java не определяет его, а загружает все классы, производные от абстрактного класса ClassLoader.Загрузчик классов делится на пользовательский загрузчик классов , как бы ни делился тип загрузчика классов, у нас самый распространенный загрузчик классов в программе всегда только 3.

image-20210807225824687

2.2.1 Загрузчик, поставляемый с виртуальной машиной

2.2.1.1, запустить загрузчик классов

Bootstrap ClassLoader также называется Bootstrap ClassLoader, он реализован на языке C/C++ и вложен в JVM, не наследуется отjava.lang.ClassLoader, без родительского загрузчика. Мы не можем получить это через код Java.

Он используется для загрузки основной библиотеки Java (JAVA_HOME/jre/lib/rt.jar, resources.jar или содержимого пути sun.boot.class.path),Используется для предоставления классов, требуемых самой JVM.. Он также загружает класс расширения и загрузчик класса приложения и назначает его загрузчиком родительского класса.

Из соображений безопасности загрузчик классов запуска Bootstrap загружает только классы, имена пакетов которых начинаются с java, javax, sun и т. д.

2.2.1.2, расширение загрузчика классов

Extension ClassLoader (Расширение ClassLoader), он написан на языке Java, автором sun.misc.Launcher$ExtClassLoaderРеализация, производная от класса ClassLoader.

Его загрузчик родительского класса — это загрузчик класса запуска, изjava.ext.dirs Загрузите библиотеку классов из каталога, указанного системным свойством, или загрузите библиотеку классов из подкаталога jre/1ib/ext (каталог расширения) каталога установки JDK.Если созданные пользователем файлы JAR помещаются в этот каталог, они также автоматически загружаются загрузчиком класса расширения.

2.2.1.3, загрузчик классов приложений

Загрузчик класса приложения также называется загрузчиком системного класса (AppClassLoader), он написан на языке java иsun.misc.LaunchersAppClassLoaderРеализация, производная от класса ClassLoader, загрузчик родительского класса является загрузчиком класса расширения.

Он отвечает за загрузку библиотеки классов по пути, указанному в переменной окружения classpath или в системном свойстве java.class.path. Эта загрузка классов является загрузчиком классов по умолчанию в программе.Все классы приложения ava загружаются им,пройти черезClassLoader#getSystemclassLoader() метод для получения загрузчика классов.

2.2.2 Пользовательский загрузчик классов

При ежедневной разработке приложений Java загрузка классов почти выполняется тремя вышеперечисленными загрузчиками классов.При необходимости мы также можем настроить загрузчик классов, чтобы настроить метод загрузки класса. Почему пользовательский загрузчик классов? Есть четыре основные причины, по которым мы настраиваем наш загрузчик классов:

  1. Загружайте классы изолированно.
  2. Измените способ загрузки классов.
  3. Разверните источник загрузки.
  4. Предотвращение утечки исходного кода.

Шаги реализации пользовательского загрузчика классов:

  1. Разработчики могут реализовать свой собственный загрузчик классов, унаследовав абстрактный класс java.lang.ClassLoader для удовлетворения некоторых особых требований.

  2. До JDK1.2 при настройке загрузчика классов он всегда наследовал класс ClassLoader и переписывал метод loadClass() для реализации пользовательского класса загрузки классов, но после JDK1.2 пользователям больше не рекомендуется переопределять loadclass( ) рекомендуется прописать собственную логику загрузки классов в методе findClass().

  3. При написании пользовательского загрузчика классов, если нет слишком сложных требований, вы можете напрямую наследовать класс URLClassLoader, чтобы не писать метод findClass() и способ получения потока байт-кода самостоятельно, чтобы пользовательский загрузчик классов может писать более лаконично.

2.3 Инструкции по использованию ClassLoader

Класс ClassLoader является абстрактным классом, и все последующие загрузчики классов наследуются от ClassLoader (за исключением запускаемого загрузчика классов).

имя метода описывать
getParent() Возвращает загрузчик суперкласса этого загрузчика класса
loadClass(String name) Загружает класс с именем name, возвращая экземпляр java.lang.Class
findClass(String name) Найдите класс с именем name, возвращающий экземпляр java.lang.Class
findloadedClass(String name) Находит загруженный класс с именем name и возвращает экземпляр java.lang.Class
defineClass(String name , byte[] b , int off , int len) Преобразуйте содержимое массива байтов b в класс Java и верните экземпляр java.lang.Class.
resloveClass(Class<?> c) Указывает соединение с классом Java

    sun.misc.Launcher Это входное приложение виртуальной машины Java.

类加载器继承关系

Если мы хотим получить ClassLoader, есть примерно четыре способа.

Способ 1: получить текущий ClassLoader

clazz.getClassLoader()

Способ 2: получить ClassLoader текущего контекста потока

	Thread.currentThread().getContextClassLoader()

Способ 3: получить ClassLoader системы

ClassLoader.getSystemClassLoader()

Способ 4: получить ClassLoader вызывающего объекта

DriverManager.getCallerClassLoader()

2.4. Механизм родительского делегирования

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

Это работает примерно так:

双亲委派机制

  1. Когда загрузчик классов получает запрос на загрузку класса, он не загружает его сам, а делегирует запрос загрузчику Fred для выполнения.
  2. Если у родительского загрузчика классов есть другие родительские загрузчики классов, он делегирует дальше, рекурсивно, пока запрос, наконец, не достигнет стартового загрузчика классов верхнего уровня.
  3. Если загрузчик родительского класса может завершить задачу загрузки этого класса, он завершится успешно. Если загрузчик родительского класса не может выполнить задачу по загрузке класса, загрузчик дочернего класса попытается загрузить его самостоятельно.

2.4.1. Народный язык объясняет механизм родительского делегирования.

Если есть ребенок, у него кислое и жесткое яблоко, в это время дома бабушка и мама.

2.4.1.1, родительский класс успешно загружен

双亲委派机制大白话-父类成功加载

2.4.1.2, не удалось загрузить родительский класс

双亲委派机制白话-父类加载失败

2.4.2 Доказать существование родительского делегирования

Мы создаем новый пакет в нашем проекте с именемjava.langизStringКласс должен быть JDK8, а не JDK11, иначе его невозможно проверить.

package java.lang;

/**
 * @Description 这是我们自己定义的Stirng,和java官方的String同包同名
 * @Author XiaoLin
 * @Date 2021/8/28 16:14
 */
public class String {
    // 在对象初始化之前就会执行
  static {
    System.out.println("我是XiaoLin定义的String");
  }
  
}

Давайте создадим новый тестовый класс

package cn.linstudy;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/8/28 16:15
 */
public class TestString {

  public static void main(String[] args) {
    java.lang.String str = new java.lang.String();
    System.out.println("我是XiaoLin");
  }
}

image-20210828162242243

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

мы вStirngНапишите основной метод в классе и выполните его.

package java.lang;

/**
 * @Description 这是我们自己定义的Stirng,和java官方的String同包同名
 * @Author XiaoLin
 * @Date 2021/8/28 16:14
 */
public class String {
  static {
    System.out.println("我是XiaoLin定义的String");
  }

  public static void main(String[] args) {
    System.out.println("Hello XiaoLin");
  }
}

image-20210828162450009

Нашел ошибку, сказал, что не может найтиmainметод, снова объясняя, что выполнение не то, что мы определилиStirngкласс, но официальная JavaStirngДобрый.

2.4.3. Преимущества механизма родительского делегирования

Поскольку у JDK есть механизм родительского делегирования, у него должны быть свои уникальные преимущества.Его преимущества заключаются в двух моментах:

  1. Избегайте повторной загрузки классов. Пока один загрузчик классов загружает этот класс, этот класс не будет загружаться другими загрузчиками классов, что предотвращает повторную загрузку классов.
  2. Защитите безопасность программы и предотвратите подделку основных API. Возьмем пример, настроим класс, вJava.langпод пакет.
package java.lang;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/8/28 16:34
 */
public class SweetCode {

  public static void main(String[] args) {
    System.out.println("Hello SweetCode");
  }
}

image-20210828163736527

2.4.4. Механизм безопасности песочницы

Мы сами настроили класс String, но при загрузке пользовательского класса String мы сначала будем использовать загрузчик классов начальной загрузки для загрузки, а загрузчик классов начальной загрузки сначала загрузит файлы, которые поставляются с jdk (пакет rt.jar) во время загрузки В java\lang\String.class) в сообщении об ошибке говорится, что нет основного метода, так как загружен класс строки в пакете rt.jar. Это обеспечивает защиту исходного кода ядра Java, что является механизмом безопасности песочницы.