Детоксикация JVM — подсистема загрузки классов

JVM
Детоксикация JVM — подсистема загрузки классов

Обучение с помощью вопросов, особенно вопросов интервью, является наиболее эффективным. Давай, Олли!

Нравится + Избранное, чтобы узнать серию, статьи включены в GitHubJavaEgg, N-линия Интернет-разработка основных навыков спектра оружия

прямое интервью

  1. Видя, что ваше резюме знакомо с JVM, можете ли вы рассказать о процессе загрузки классов?
  2. Можем ли мы настроить класс String для использования?
  3. Что такое загрузчик классов и что такое загрузчики классов?
  4. В случае многопоточности, почему загрузка классов не выглядит многократно загруженной?
  5. Каков механизм родительского делегирования? Какое у него преимущество? Можно ли сломать этот механизм?


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

Концепция механизма загрузки классов

Виртуальная машина Java загружает данные, описывающие класс, из файла Class в память, проверяет, преобразует, анализирует и инициализирует данные и, наконец, формирует тип Java, который может быть непосредственно использован виртуальной машиной.Это механизм загрузки. виртуальной машины.. После того, как файл класса загружен загрузчиком класса, в JVM будет сформирован объект метаинформации, описывающий структуру класса.Через объект метаинформации может быть известна информация о структуре класса: такая как конструкторы, свойства и Java позволяет пользователям использовать этот объект метаинформации, связанный с классом, косвенно вызывает функцию объекта класса, вот класс класса, который мы часто видим.

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

  • Подсистема загрузки классов отвечает за загрузку файлов классов из файловой системы или сети.Файл класса имеет определенный идентификатор файла (0xCAFEBABE) в начале файла.
  • ClassLoader отвечает только за загрузку файлов классов. Что касается того, может ли он работать, это зависит от Execution Engine.
  • Информация о загруженном классе хранится в области памяти, называемой областью методов. В дополнение к информации о классе в области метода также хранится информация о пуле констант времени выполнения, возможно, включая строковые литералы и числовые константы (эта часть информации о константах является картой памяти части пула констант в файле класса).
  • Объекты класса хранятся в области кучи

Роль ClassLoader ClassLoader

  1. Файл класса существует на локальном жестком диске, который можно понимать как шаблон, нарисованный дизайнером на бумаге, и, наконец, шаблон загружается в JVM, когда он выполняется для создания n идентичных экземпляров в соответствии с этим файлом.
  2. Файл класса загружается в JVM, называется шаблоном метаданных ДНК и помещается в область методов.
  3. В файле .calss -> JVM -> со временем становится шаблоном метаданных, для этого процесса требуется транспортный инструмент (загрузчик классов), играющий роль курьера

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

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

jvm-class-load

1. Загрузка:

  1. Получить двоичный поток байтов, который определяет класс по его полному имени
  2. Преобразуйте статическую структуру хранения, представленную этим потоком байтов, в структуру данных времени выполнения области метода.
  3. Генерирует представление этого класса в памятиjava.lang.Classобъект, как запись доступа к различным данным этого класса в области методов

нагрузка.calssпуть файла

  • Загрузка напрямую из локальной системы
  • Получено через сеть, типичный сценарий: веб-апплет
  • Чтение из сжатых файлов zip станет основой для будущих форматов jar и war.
  • Генерация расчетов во время выполнения, наиболее используемая из них: технология динамического прокси.
  • Генерируется из других файлов, таких как JSP-приложения.
  • Извлечение файлов .class из проприетарных баз данных, что встречается относительно редко.
  • Получено из зашифрованных файлов, типичные меры защиты от декомпиляции файлов классов

2. Связывание

Подтвердить (подтвердить)

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

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

Подготовить

  • Выделите память для переменной класса и установите начальное значение по умолчанию для переменной класса, т.е.нулевое значение

    тип данных нулевое значение
    int 0
    long 0L
    short (short)0
    char '\u0000'
    byte (byte)0
    boolean false
    float 0.0f
    double 0.0d
    reference null
  • Статическое изменение с окончательным здесь не включено, потому что окончательный будет выделен во время компиляции, а инициализация будет отображаться на этапе подготовки.

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

    private static int i = 1;  //变量i在准备阶只会被赋值为0,初始化时才会被赋值为1
    private final static int j = 2;  //这里被final修饰的变量j,直接成为常量,编译时就会被分配为2
    

Решать

  • Процесс преобразования символической ссылки в пуле констант в прямую ссылку
  • На самом деле операция синтаксического анализа часто сопровождается инициализацией JVM после выполнения.
  • Символическая ссылка представляет собой набор символов для описания объекта, на который делается ссылка. Буквальная форма символических ссылок четко определена в формате файла класса спецификации виртуальной машины Java. Прямая ссылка — это указатель непосредственно на цель, относительное смещение или маркер, который косвенно расположен на цели.
  • Действия синтаксического анализа в основном относятся к классам или интерфейсам, полям, методам классов, методам интерфейса, типам методов и т. д. соответствующий постоянному пулуCONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_infoЖдать

3. Инициализация

  • Фаза инициализации заключается в выполненииметод конструктора классаПроцесс ()
  • Этот метод не нуждается в определении, он представляет собой комбинацию действия присваивания всех переменных класса в классе, автоматически собираемого компилятором javac, и операторов в статическом блоке кода.
  • Инструкции в методе-конструкторе выполняются в том порядке, в котором операторы появляются в исходном файле.
  • () отличается от конструктора класса (конструктор () с точки зрения виртуальной машины)
  • Если у класса есть родительский класс, JVM гарантирует, что () родительского класса был выполнен до того, как будет выполнен () дочернего класса.
  • Виртуальная машина должна гарантировать, что метод () класса синхронизирован и заблокирован при многопоточности.
public class ClassInitTest{
  private static int num1 = 30;
  static{
    num1 = 10;
    num2 = 10;     //num2写在定义变量之前,为什么不会报错呢??
    System.out.println(num2);   //這裡直接打印可以吗? 报错,非法的前向引用,可以赋值,但不可调用
  }
  private static int num2 = 20;  //num2在准备阶段就被设置了默认初始值0,初始化阶段又将10改为20
  public static void main(String[] args){
    System.out.println(num1);  //10
    System.out.println(num2);   //20
  }
}

Активное и пассивное использование классов

Использование классов в программах Java делится на: активное использование и пассивное использование. Спецификации виртуальной машиныЕсть и всего 5 случаев, когда класс надо сразу "инициализировать", то есть активное использование класса.

  • Создайте экземпляр класса, получите доступ к статической переменной класса или интерфейса, или присвойте значение статической переменной, или вызовите статический метод класса (то есть при встрече с четырьмя инструкциями байт-кода new, getstatic, putstatic , и invokestatic)
  • отражение
  • Инициализировать подкласс класса
  • Класс, отмеченный как класс запуска при запуске виртуальной машины Java.
  • Поддержка динамических языков, начиная с JDK7:java.lang.invoke.MethodHandleрезультат разбора экземпляра,REF_getStatic,REF_putStatic,REF_invokeStaticЕсли класс, соответствующий дескриптору, не инициализирован, он будет инициализирован

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

eg:

public class NotInitialization {
    public static void main(String[] args) { 
        //只输出SupperClass int 123,不会输出SubClass init
        //对于静态字段,只有直接定义这个字段的类才会被初始化
        System.out.println(SubClass.value); 
    }
}

class SuperClass {
    static {
        System.out.println("SupperClass init");
    }
    public static int value = 123;
}

class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init");
    }
}

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

  • JVM поддерживает два типа загрузчиков классов, а именно Bootstrap ClassLoader и определяемый пользователем ClassLoader.

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

Загрузчик классов загрузки (загрузчик классов начальной загрузки, Bootstrap ClassLoader)

  • Эта загрузка классов реализована на языке C/C++, вложенном в JVM.
  • Он используется для загрузки основной библиотеки Java (JAVA_HOME/jre/lib/rt.jar,resource.jarилиsun.boot.class.pathсодержимое по пути), используемое для предоставления классов, требуемых самой JVM.
  • не наследуется отjava.lang.ClassLoader, нет родительского загрузчика
  • Загрузить класс расширения и загрузчик класса приложения и назначить их загрузчиком родительского класса.
  • По соображениям безопасности загрузчик классов запуска Boostrap загружает только классы, начинающиеся с java, Javax, sun и т. д.

Расширение ClassLoader

  • Написано на языке java авторомsun.misc.Launcher$ExtClassLoaderвыполнить
  • Получено от ClassLoader
  • Загрузчик родительского класса является загрузчиком запускаемого класса.
  • отjava.ext.dirsЗагрузите библиотеку классов из каталога, указанного системным свойством, или из каталога установки JDK.jre/lib/extЗагрузите библиотеку классов в подкаталог (каталог расширения). Если созданные пользователем файлы JAR помещаются в этот каталог, они также автоматически загружаются загрузчиком класса расширения.

Загрузчик классов приложений (также называемый загрузчиком системных классов, AppClassLoader)

  • Написано на языке java авторомsun.misc.Lanucher$AppClassLoaderвыполнить
  • Получено от ClassLoader
  • Загрузчик родительского класса является загрузчиком класса расширения.
  • Он отвечает за загрузку переменных окружения.classpathили свойства системыjava.class.pathБиблиотека классов по указанному пути
  • Загрузка классаЗагрузчик классов по умолчанию в программе, вообще классы Java-приложений подгружаются им
  • пройти черезClassLoader#getSystemClassLoader()способ получить загрузчик классов
public class ClassLoaderTest {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);  //sun.misc.Launcher$AppClassLoader@135fbaa4

        //获取其上层:扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);  //sun.misc.Launcher$ExtClassLoader@2503dbd3

        //再获取其上层:获取不到引导类加载器
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);     //null

        //对于用户自定义类来说,默认使用系统类加载器进行加载,输出和systemClassLoader一样
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);  //sun.misc.Launcher$AppClassLoader@135fbaa4

        //String 类使用引导类加载器进行加载。Java的核心类库都使用引导类加载器进行加载,所以也获取不到
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);  //null

        //获取BootstrapClassLoader可以加载的api的路径
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urls) {
            System.out.println(url.toExternalForm());
        }
    }
}

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

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

Почему пользовательский загрузчик классов?
  • классы нагрузки изолированно
  • Изменить способ загрузки классов
  • Расширенный источник загрузки (классы могут быть загружены из указанных источников, таких как база данных, облако и т. д.)
  • Предотвратить утечку исходного кода (код Java легко декомпилировать, если он зашифрован, пользовательский загрузчик может сначала расшифровать его, а затем загрузить при загрузке класса)
Шаги реализации пользовательского загрузчика
  1. Разработчики могут наследовать абстрактные классыjava.lang.ClassLoaderПуть класса, реализуйте свой собственный загрузчик классов для удовлетворения некоторых особых потребностей.
  2. До JDK1.2 при настройке загрузчика классов он всегда наследовал класс ClassLoader и переписывал метод loadClass() для реализации пользовательского класса загрузки классов, но после JDK1.2 пользователям не рекомендуется переопределять loadClass(). метод, но рекомендуется написать собственную логику загрузки класса в методе findClass()
  3. При написании пользовательского загрузчика классов, если нет слишком сложных требований, вы можете напрямую наследовать класс URLClassLoader, чтобы вы могли не писать метод findClass() и способ получения потока байт-кода самостоятельно, чтобы пользовательский класс загрузчик может написать более лаконично

Общие методы 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.
resolveClass(Class<?> c) Подключиться к указанному классу Java

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

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


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

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

процесс работы

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

Преимущество

  • Избегайте повторной загрузки классов. В JVM разные классы различаются не только по имени класса. Один и тот же файл класса, загруженный разными загрузчиками классов, принадлежит двум разным классам (например, класс Object в Java, независимо от того, какой загрузчик классов используется) Чтобы загрузить этот класс, он в конечном итоге делегируется загрузчику классов запуска в верхней части модели для загрузки.Если модель родительского делегирования не используется, и каждый загрузчик классов загружается сам по себе, будет много разных классов объектов. в системе)
  • Защитите безопасность программы, предотвратите произвольное вмешательство в основной API и предотвратите динамическую замену написанными пользователями классами некоторых основных классов Java, таких как наш пользовательский класс: java.lang.String.

В JVM есть два необходимых условия, чтобы указать, являются ли два объекта класса одним и тем же классом:

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

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

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

Нарушение модели родительского делегирования

  • Модель родительского делегирования — это не обязательная модель ограничений, а реализация загрузчика классов, рекомендованная дизайнерами Java для разработчиков, которая может быть «сломанной», пока мы настраиваем загрузчик классов,Переопределить метод loadClass(), указание новой логики загрузки прерывает работу, а переопределение метода findClass() не прерывает делегирование родительских функций.
  • Существует проблема с родительской моделью делегирования: загрузчик классов верхнего уровня не может загрузить классы загрузчика классов нижнего уровня. Типичными примерами являются JNDI и JDBC, поэтому добавлен загрузчик класса контекста потока (Thread Context ClassLoader), доступ к которому можно получить черезThread.setContextClassLoaser()Установите загрузчик классов, а затем используйте ClassLoader верхнего уровня.Thread.getContextClassLoader()Получите базовый ClassLoader для загрузки.
  • Tomcat использует собственный ClassLoader, а также нарушает родительский механизм делегирования. Каждое приложение использует WebAppClassloader для отдельной загрузки.Сначала оно использует WebAppClassloader для загрузки классов.Если оно не может быть загружено, оно поручает загрузку родительскому загрузчику, чтобы гарантировать, что классы в каждом приложении не конфликтуют. В каждом tomcat можно развернуть несколько проектов, и в каждом проекте много одинаковых файлов классов (много одинаковых пакетов jar), и их можно загрузить в jvm, не мешая друг другу.
  • Используйте сломанное родительское делегирование для достижениякод горячей замены(Каждый раз, когда вы изменяете файл класса, вам не нужно перезапускать службу). Поскольку Class может быть загружен ClassLoader только один раз, в противном случае он сообщитjava.lang.LinkageError. Когда мы хотим реализовать горячее развертывание кода, мы можем каждый раз создавать новый пользовательский ClassLoader для загрузки новых файлов классов. С помощью этой функции достигается реализация динамической модификации JSP.

Ссылка: «Глубокое понимание виртуальной машины JVM» «JVM Shang Silicon Valley»