Изображение и текст полностью понимают загрузчик классов и механизм родительского делегирования.

JVM

Обсуждаете ли вы технические вопросы с коллегами, одноклассниками, начальством, сверстниками или интервьюерами, легко принять участиеМасштабная сцена разрыва JVM. Для того, чтобы все от масштабной сцены разрывавыделяться, В последнее время я много думал о том, как представить точки знаний как можно проще для понимания и запоминания. Так началось написание этой серии статей. Чтобы точки знаний, связанные с JVM, могли формироватьсистема, художественное мышление соберет и организует серию тем, чтобы максимально подробно описать соответствующие точки знаний в картинках и, наконец, объединить все соответствующие точки знаний в картинку. Постоянно обновляется, добро пожаловать на чтение. Если есть какие-либо ошибки, пожалуйста, поднимите руку, чтобы помочь исправить меня, спасибо!

Руководство:

  1. Как создаются загрузчики классов?
  2. Каков механизм родительского делегирования? Зачем этот механизм?
  3. Находятся ли экземпляры класса и загрузчики классов в куче Java или в области методов?

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

Различные интересные и мощные функции могут быть реализованы с помощью пользовательских загрузчиков классов: OSGi, горячее развертывание, шифрование кода и т. д.

1. Процесс загрузки загрузчика классов

image-20200105144705999

На приведенном выше рисунке показан процесс загрузки загрузчика классов.

Вот краткое описание:

1.1, запустите загрузчик классов

** Загрузчик класса запуска: ** При запуске системы он сначала загрузит пакет jar в каталог /lib через загрузчик класса запуска, реализованный C++, или путь, указанный параметром -Xbootclasspath и виртуальной машиной. Идентифицированное имя файла пакета jar. Загрузите соответствующий класс в область метода.

Этот шаг загрузит ключевой класс:sun.misc.Launcher. Этот класс содержит два статических внутренних класса:

  • ExtClassLoader: расширяет внутренний класс загрузчика классов, о котором будет сказано ниже;
  • AppClassLoader: внутренний класс загрузчика классов приложения, который будет обсуждаться ниже.

Вы можете декомпилировать файл rt.jar для просмотра подробного кода:

image-20200105124613663

image-20200105131342939

После загрузки в класс Launcher класс будет инициализирован.В процессе инициализации будут созданы ExtClassLoader и AppClassLoader.Исходный код выглядит следующим образом:

public Launcher() {
    ExtClassLoader extClassLoader;
    try {
      extClassLoader = ExtClassLoader.getExtClassLoader();
    } catch (IOException iOException) {
      throw new InternalError("Could not create extension class loader", iOException);
    }
    try {
      this.loader = AppClassLoader.getAppClassLoader(extClassLoader);
    } catch (IOException iOException) {
      throw new InternalError("Could not create application class loader", iOException);
    }
    Thread.currentThread().setContextClassLoader(this.loader);
    ...

Поскольку загрузчик класса запуска реализован на C++, поэтомуЗагрузчик класса запуска недоступен в коде Java, если попытаться пройтиString.class.getClassLoader()Получите ссылку на класс запуска, который вернетnull;

проблема:

  1. Кто загружает загрузчик классов запуска, загрузчик классов расширений и загрузчик классов приложений?

    1. Загрузчик класса запуска является внутренней реализацией JVM.После того, как JVM выделяет память, загрузчик класса запуска создается JVM.
    2. Загрузчик класса расширения и загрузчик класса приложения загружаются загрузчиком класса запуска;
  2. Скажите, что выводит следующий код:

 public static void main(String[] args) {
     System.out.println("加载当前类的加载器:" + TestClassLoader.class.getClassLoader());
        System.out.println("加载应用程序类加载器的加载器"
                         + TestClassLoader.class.getClassLoader().getClass().getClassLoader());
        System.out.println("String类的启动类加载器" + String.class.getClassLoader());
   }

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

Как показано выше, загрузчик классов расширения отвечает за загрузку классов в каталог /lib/ext или по пути, указанному в системной переменной java.ext.dirs.

1.3. Загрузчик классов приложения

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

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

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

2. Родительский механизм делегирования загрузчика классов

2.1. Принцип механизма родительского делегирования

Модель родительского делегирования была введена после JDK1.2 и широко использовалась.Это не обязательная модель ограничений.Это реализация загрузчика классов, рекомендованная дизайнерами Java для разработчиков. Мы также можем переопределить соответствующий метод и реализовать собственную модель загрузки.

Механизм родительского делегирования загрузчика классов выглядит следующим образом:

image-20200105170731274

Это:

  • Когда загрузчик класса получает запрос на загрузку класса, он не будет пытаться загрузить класс немедленно, а делегирует запрос родительскому загрузчику для его завершения. Это относится к каждому уровню. Все домашние запросы в конечном итоге передаются на верхний уровень. класс уровня.загрузчик для обработки;
  • Если родительский загрузчик не существует, попытайтесь определить, был ли он загружен загрузчиком запускаемого класса;
  • Если он не пойман, попробуйте загрузить его самостоятельно.

проблема:

  1. Почему существует такой сложный механизм родительского делегирования?
    1. Если такого механизма нет, мы можем подправить нужные классы в загрузчике классов автозапуска, например, написатьjava.lang.ObjectИспользуя собственный загрузчик классов для загрузки, в системе будет несколько классов объектов, поэтому нельзя гарантировать самое базовое поведение системы типов Java.

2.2 Поток обработки механизма родительского делегирования

Каков процесс родительского делегирования по умолчанию в JDK? Далее мы смотрим на код, следующееjava.lang.ClassLoader.loadClass()Реализация метода:

    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;
        }
    }

Преобразованный в блок-схему, то есть:

image-20200105174045231

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

image-20200105195158889

Модель родительского делегирования имеет следующие характеристики:

  • Принцип видимости:
    • Загрузчик классов приложения может считывать классы, загруженные загрузчиком классов расширения и загрузчиком классов запуска;
    • Загрузчик класса расширения может прочитать класс, загруженный загрузчиком класса запуска;
  • Уникальность:
    • Класс уникален, дублирующихся классов нет;

2.3 Отступление о загрузчиках классов и экземплярах класса

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

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

Как показано ниже:

image-20200105200625589

Загруженная информация о классе хранится в области методов, а область методов одновременно использует два типа заданий:

  • **Загрузчик классов:** отвечает за управление информацией о классе в каждой области хранения, например за загрузку и выгрузку информации о классе;
  • ** Экземпляр класса: ** Отвечает за стыковку внешних требований, если кто-то извне хочет просмотреть информацию о классе внутри, ему необходимо получить ее через экземпляр класса;

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

3. Другие случаи модели без родительского делегирования

3.1, устаревшие проблемы JDK 1.0

Класс ClassLoader уже существовал в JDK1.0, но в то время не было родительского механизма делегирования.Чтобы настроить загрузчик классов, пользователям необходимо повторноloadClass()метод, и мы знаем, что после JDK1.2 loadClass является кодом реализации родительского механизма делегирования.В настоящее время, чтобы реализовать собственный загрузчик классов, вам нужно снова найти класс Class().

Если метод loadClass() переработан, это означает, что родительская модель делегирования больше не используется.

3.2, загрузчик класса контекста потока

Зачем нужна эта штука, начнем с чехла.

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

Мы знаем, что в структуре каталогов Tomcat есть следующие каталоги:

  • /общий/:Библиотека классов в этом каталоге может использоваться Tomcat и всеми веб-приложениями;

  • /сервер/:Библиотека классов в этом каталоге может использоваться Tomcat, но не видна для всех веб-приложений;

  • /общий/:Библиотека классов в этом каталоге может использоваться всеми веб-приложениями, но не видна самому Tomcat;

Кроме того, веб-приложение имеет собственную библиотеку классов, которая размещена в/WebApp/WEB-INFВ каталоге: библиотеки классов в этом каталоге могут использоваться только этим веб-приложением и не видны Tomcat или другим веб-приложениям. Чтобы добиться эффекта видимости библиотеки классов для каждого из вышеуказанных каталогов, Tomat предоставляет следующие пользовательские загрузчики классов:

image-20200105205509075

Теперь следующий сценарий:

Мы обнаружили, что в Tomcat есть несколько веб-приложений, и каждое веб-приложение использует Spring, поэтому мы поместили пакет spring jar вsharedв каталоге.

Итак, возникает проблема: поскольку пакет jar Spring загружается загрузчиком класса Shared, предположим, мы хотим использовать метод getBean SpringContext для получения Bean в веб-приложении.Если он основан на модели родительского делегирования, будет проблема, потому что класс webapp The Java невидим для SharedClassLoader:

image-20200105213630571

Загрузчик классов контекста потока в Spring

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

Для интерпретации исходного кода инициализации Spring обратитесь к моей статье здесь:Анализ принципов Spring IoC.

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

В ApplicationContext есть одинbeanClassLoaderПоле, это загрузчик класса бина, инициализированный в методе prepareBeanFactory():

beanFactory.setBeanClassLoader(getClassLoader());

Метод getClassLoader выглядит следующим образом:

	@Override
	@Nullable
	public ClassLoader getClassLoader() {
		return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
	}

Метод ClassUtils.getDefaultClassLoader():

	@Nullable
	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}

Можно обнаружить, что ClassLoader в контексте текущего потока наконец-то взят здесь.

загрузить бобы

Давайте посмотрим на код, где Spring загружает класс. Здесь мы непосредственно находим метод создания экземпляров синглтонов и продолжаем искать код, который требует внимания:

Мы обнаружили, что этот метод вызывается при загрузке класса компонента:

AbstractBeanFactory:

ClassLoader beanClassLoader = getBeanClassLoader();

То есть он используется в ApplicationContextbeanClassLoader, загрузчик класса контекста потока для загрузки экземпляра класса компонента.

Суммировать

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

3.3 Горячее развертывание модуля

В основном это модульная технология горячего развертывания, такая как OSGi. В OSGi это уже не древовидная структура в родительской модели делегирования, а более сложная сетчатая структура.

References

Where are static methods and static variables stored in Java?

ClassLoader in Java

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

«Глубокое понимание виртуальной машины Java — расширенные функции и лучшие практики JVM»

Chapter 5. Loading, Linking, and Initializing


Эта статьяarthinkingОн написан на основе соответствующей технической информации и официальных документов для обеспечения точности содержания.Если вы обнаружите какие-либо ошибки или упущения, пожалуйста, поднимите руки, чтобы помочь их исправить, я очень благодарен.

Вы можете следить за моим блогом:itzhai.comДля других статей я буду продолжать обновлять технологии, связанные с серверной частью, включая JVM, основу Java, проектирование архитектуры, сетевое программирование, структуры данных, базы данных, алгоритмы, параллельное программирование, распределенные системы и другой связанный контент.

Если вы чувствуете, что прочитали эту статью, вы можете关注мой аккаунт или点赞Да, именно ваша поддержка мотивирует меня писать! Подпишитесь на мой официальный аккаунт, чтобы получать свежие статьи вовремя.


Автор этой статьи: артмышление

Ссылка на блог:woohoo.ithome.com/JVM/что такое…

Изображение и текст полностью понимают загрузчик классов и механизм родительского делегирования.

Заявление об авторских правах: Авторские права принадлежат автору и не могут быть воспроизведены без разрешения Нарушение должно быть расследовано! Чтобы связаться с автором, пожалуйста, добавьте публичный аккаунт.