Загрузчик базового класса JVM

задняя часть JVM
Загрузчик базового класса JVM

Введение

На этот раз я представляю загрузчик классов, который содержит много контента, включая родительское делегирование, анализ кода, spi и т. д. Надеюсь, вы хорошо проведете время, изучая

Поддерживающее видео объяснение:воооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо(Обновления могут запаздывать, но не отсутствовать)

Предварительное понимание

роль загрузчика классов

Мы знаем, что java-код должен быть сначала скомпилирован в байт-код класса, а затем загружен в jvm.Чем он загружается? Эта "вещь" является загрузчиком классов

image.png

Классификация загрузчика классов

Загрузчики классов, предоставляемые jdk,

  • Bootstrap ClassLoader
  • Расширение ClassLoader
  • Загрузчик классов приложений

Разделение труда загрузчика классов

Разделение труда: фактически каждый загрузчик классов загружает контент только по определенному пути.

загрузчик класса приложения: загрузить все файлы классов в ClassPath. Подробнее

image.png

Загрузчик класса расширения:Загрузка jre/lib/ext/

image.png

Запустите загрузчик классовЗагрузка /jre/lib/rt.jar

image.png

image.png

родительская делегация

Поговорите с классовым погрузчиком, мы должны упомянуть «Механизм делегата родителей», родители делегируют слово звуки неясным, на самом деле очень просто. Как будет оценено, сотрудничество между режимом классов погрузчика, понимая следующий текст в сочетании с фиг.

Если вам нужно загрузить класс сейчас (например, написать новый тест(), то загрузите класс Test)

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

image.png

(Предположим, что загрузчик класса приложения сейчас не найден, поэтому достигнут загрузчик класса расширения)

  1. Загрузчик класса расширения также будет искать в своем собственном кеше. Если он может найти его, верните его напрямую. Если он не может найти его, пусть его найдет загрузчик запускаемого класса.

image.png(Предположим, что загрузчик классов расширения сейчас не найден, поэтому пришло время запустить загрузчик классов)

  1. Загрузчик классов запуска также будет искать его в кеше, если найдет — вернется, если не найдет — пойдет по тому пути, за который отвечает (/jre/li/rt.jar). «Горшок» в загрузчик класса расширения

image.png

4. Расширенный загрузчик классов также будет искать путь, за который он отвечает (jre/lib/ext/), и если не найдет, то продолжать кидать горшок обратно загрузчику классов приложения

image.png

  1. Загрузчик классов приложения просматривает путь к классам и выдает исключение ClassNotFound, если оно не найдено.

image.png

Советы:Отношения родитель-потомок в загрузчике классов являются логическими отношениями родитель-потомок, а не расширением в коде.

Почему родительское делегирование

Безопасно, безопасно, еще безопасно.

Десятки тысяч строк кода, безопасность превыше всего.

Только представьте, что если вы сами определяете класс java.lang.String и кладете его в путь к классам, очень опасно, если он напрямую загружается в память загрузчиком классов приложения, потому что это означает, что вы можете заменить исходный jdk в таким образом.Класс String. Затем проделайте из него какие-нибудь трюки, например, (переведите деньги на свою банковскую карту)

image.png

посмотрите на исходный код

Мы делаем еще один шаг, изучая исходный код и глубоко понимая механизм родительского делегирования. Далее я вырезаю код положения ключа для пояснения. Пожалуйста, объедините теоретическое содержание первой половины, чтобы понять

С++ код

Поскольку java работает на jvm, чтобы приблизиться к источнику проблемы, давайте посмотрим на код части c++.

кредитMainCLass: судя по названию, это класс, который загружает основной метод в наш java-код. (файл java.c)

image.png

После входа в loadMainCLass видно, что вызывается метод GetStaticMethodID и передается параметр checkAndLoadMain, это водораздел, а дальше он входит в java-код.image.png

Еще одно слово:GetStaticMethodID получает идентификатор основной статической функции.

Java-часть

Тип scloader — AppClassLoader (загрузчик класса приложения).

image.png

Давайте сначала посмотрим на AppClassLoader

Он наследует UrlClassLoaderimage.png

UrlClassLoader наследует SecureClassLoaderimage.png

SecureClassLoader наследует ClassLoaderimage.png

разбираться

image.png

Возвращаясь к предыдущему loadClass, AppClassLoader, URLClassLoader, SecureClassLoader, ни один из них не переписывал loadClass, поэтому в настоящее время вызывается loadClass в ClassLoader.

Давайте посмотрим на loadClass

image.png

Вызывается LoadClass(name, false), этот метод был переопределен AppClassLoader, поэтому в центре внимания AppClassLoader находится LoadClass

image.pngНаконец, этот метод вызывает LoadClass родительского класса, которым является ClassLoader.

Ниже приведен метод LoadClass в ClassLoader, Лично я считаю, что следующий код особенно доработан. Давайте вместе поклоняться мышлению великого бога

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

findLoadedClass: найти загруженный класс, поскольку он еще не загружен, поэтому здесь он должен быть нулевым.

image.png

Позвольте «родительскому» загрузчику загрузиться, вы можете видеть, что тип родительского загрузчика — ExtClassLoader (загрузчик расширенного класса)

image.png

Давайте посмотрим на код ExtClassLoader.

image.png

image.png

Его отношение наследования очень похоже на AppClassLoader.

image.png

Вернитесь к parent.loadClass, потому что ExtClassLoader также наследует ClassLoader, поэтому он, наконец, выполняется в этом месте, но обратите внимание, что в данный момент это ExtClassLoader.

image.png

image.png

Очевидно, что findLoadedClass ExtClassLoader также имеет значение null, поэтому он перейдет к findBootstrapClassOrNull.

image.png

Советы:Родительским загрузчиком ExtClassLoader является BootStrap, написанный на C++ Как видно из рисунка ниже, findBootstrapClassOrNull, наконец, вызывает нативный метод

image.png

Класс, который будет загружен в это время, написан нами и должен быть загружен загрузчиком классов приложения, поэтому findBootstrapClassOrNull вызовет исключение ClassNotFoundException.

image.png

После того, как исключение перехвачено, ExtClassloader пытается загрузить класс в каталог, за который он отвечает, но, естественно, не может его загрузить и в конце выдает исключение

image.png

После того, как загрузчик класса приложения поймает исключение, он также попытается загрузить сам класс (из пути к классам), потому что наш класс существует в пути к классам, поэтому его можно загрузить, иначе он выдаст исключение ClassNotFoundException.

image.png

На данный момент код, делегированный родителями, в основном тот же. Давайте еще раз посмотрим, как родителю присваивается значение

вернуться к исходной начальной точке

image.png

Посмотрите, где определен scloader,

image.png

Посмотрите еще раз на getSystemClassLoader

image.png

Внутри есть строка sun.misc.Launcher.getLauncher();image.png

см. конструктор

image.png

ExtClassLoader и AppClassLoader создаются здесь

image.png

Из этого вы можете видеть, что родитель ExtClassLoader имеет значение nullimage.png

Родителем AppClassLoader является ExtClassLoader.

image.png

image.png

Код для этой важной части почти такой же

spi

Обход родительской делегирования

Прежде чем говорить о SPI, разберемся с механизмом: когда наш класс A использует другой класс B, предполагая, что этот класс B ранее не был загружен, то в это время должен быть загружен класс B, а для загрузки класса следует использовать загрузчик классов Б., тогда какой загрузчик использовать? Фактически используется загрузчик класса А.

image.png

Тогда это создаст проблему (в качестве примера возьмем jdbc)

Драйвер загружается классом запуска в rt.jar, но драйверы базы данных, предоставляемые различными производителями, реализованы в пути к классам, и загрузчик класса запуска не может быть загружен.

image.png

Чтобы решить эту проблему, вы можете использовать spi для обхода родительского делегирования.Когда мы смотрим на исходный код, мы фактически получаем загрузчик класса контекста потока.

image.png

image.png

демонстрация спи

Простая демонстрация того, как использовать spi, без конкретных объяснений (поскольку это не является предметом этой статьи).

написать интерфейс

image.png

Напишите любой класс реализации

image.png

Создайте новый файл в этом каталоге, имя — это полное имя класса интерфейса, а содержимое — это полное имя класса класса реализации.

image.png

Просто проверьте это

image.png

На этот раз все, пока.