Привет читатели! В предыдущей статье серии JVM разработчики узнали о виртуальной машине Java (JVM) и ее архитектуре. Этот учебник поможет разработчикам правильно ответить на вопросы по следующим темам:
- Подсистема
ClassLoader
- Области данных времени выполнения
1. Введение
Прежде чем двигаться дальше, давайте взглянем на виртуальную машину Java и ее основные характеристики.
1.1 Что такое виртуальная машина Java (JVM)?
Виртуальная машина Java (JVM) — это абстрактная виртуальная машина, которая находится на вашем компьютере и обеспечивает среду выполнения для выполнения байт-кода Java. JVM доступна для многих аппаратных и программных платформ, но немногие разработчики Java знают, что Java Runtime Environment (JRE) является реализацией виртуальной машины Java (JVM). JVM анализирует байт-код, интерпретирует его и выполняет тот же байт-код для отображения выходных данных.
Основная функция JVM — выполнять скомпилированные .class
(т.е. байт-код) и генерировать вывод. Обратите внимание , что каждая операционная система имеет свою JVM, но сгенерированный вывод байт-кода одинаков во всех операционных системах. Это означает, что байт-код, сгенерированный в ОС Windows, также может работать в ОС Linux и наоборот, что делает Java независимым от платформы языком.
1.1.1 Что делает JVM?
Виртуальная машина Java выполняет следующие операции:
- Загрузка необходимых файлов
.class
и jar - Присвоение ссылок и проверка кода
- Исполнение кода
- Предоставляет среду выполнения для байт-кода Java
1.1.2 Внутренняя архитектура JVM
На следующей диаграмме показаны ключевые внутренние компоненты виртуальной машины Java, соответствующие спецификации JVM.
Компоненты загрузчика классов и областей данных времени выполнения, показанные на рис. 2, поясняются ниже.
1.2 Подсистема ClassLoader
Подсистема загрузчика классов является важным ядром виртуальной машины Java и используется для загрузки / чтения файлов .class
и сохранения байт-кода в области метода JVM. Эта подсистема обрабатывает функциональные возможности загрузки динамических классов и выполняет три основные функции, а именно:
- Загрузка . Этот компонент обрабатывает загрузку файлов
.class
из аппаратной системы в память JVM и сохраняет двоичные данные (например, полное имя класса, имя непосредственного родительского класса, информацию о методах, переменных, конструкторах и т. Д.). в областях метода. Для каждого загруженного файла.class
JVM немедленно создает объект в памяти кучи типаjava.lang.class
. Помните , что даже если разработчики вызывают класс несколько раз, будет создан только один объект класса. Существует три основных типа загрузчиков классов:- Bootstrap или Primordial ClassLoader : этот загрузчик классов отвечает за загрузку внутренних базовых java-классов, присутствующих в
rt.jar
и других классов, присутствующих вjava.lang.*
. По умолчанию он доступен для каждой JVM и написан на родных языках C / C ++. Этот загрузчик классов не имеет родителей, и если разработчики вызываютString.class.getClassLoader()
, он возвращаетnull
и любой код, основанный на этом,String.class.getClassLoader()
NullPointerException
в Java. - Расширение ClassLoader : Этот загрузчик классов является дочерним классом Primordial classloader и отвечает за загрузку классов из расширенного пути к классам (т.е.
jdk\jre\lib\ext
). Он написан на языке Java, и соответствующий файл.class
—sun.misc.Launcher$ExtClassLoder.class
- Приложение или системный загрузчик классов. Этот загрузчик классов является дочерним классом загрузчика классов Расширения и отвечает за загрузку классов из системного пути к классам. Он внутренне использует переменную среды
CLASSPATH
и написан на языке Java. Системный загрузчик классов в JVM реализован с помощьюsun.misc.Launcher$AppClassLoader.class
- Bootstrap или Primordial ClassLoader : этот загрузчик классов отвечает за загрузку внутренних базовых java-классов, присутствующих в
- Связывание : этот компонент выполняет связывание класса или интерфейса. Поскольку этот компонент включает в себя выделение новых структур данных, он может выбросить
OutOfMemoryError
и выполнить три важных действия:- Проверка : это процесс проверки двоичного представления класса и проверки, является ли созданный файл
.class
действительным или нет. Этот процесс выполняется верификатором байт-кода, и если сгенерированный файл.class
недействителен,VerifyError
- Подготовка . Это процесс выделения памяти для статических переменных уровня класса или интерфейса и назначения значений по умолчанию.
- Решение . Это процесс замены символьных ссылок исходными ссылками на память из области метода.
- Проверка : это процесс проверки двоичного представления класса и проверки, является ли созданный файл
- Инициализация : этот компонент выполняет заключительную фазу загрузки класса, когда всем статическим переменным присваиваются исходные значения, а статические блоки выполняются от родительского к дочернему классу. Этот процесс требует тщательной синхронизации, поскольку JVM является многопоточным, и некоторые потоки могут пытаться инициализировать один и тот же класс или интерфейс в одно и то же время.
1.2.1 Как ClassLoader работает в Java?
Classloader в Java работает в трех принципах: делегирование , видимость и уникальность .
- Делегация : Согласно этому:
- Всякий раз, когда виртуальная машина сталкивается с классом, JVM проверяет, загружен ли указанный файл
.class
или нет. - Если файл
.class
уже загружен в область метода, JVM рассмотрит этот класс. Если нет , JVM запрашивает подсистему загрузчика классов для загрузки этого конкретного класса - Система загрузчика классов передает запрос загрузчику классов приложения, который, в свою очередь, делегирует этот запрос загрузчику классов расширения. Загрузчик классов Extension снова делегирует этот запрос Первичному загрузчику классов.
- Первоначальный загрузчик классов будет искать этот класс в начальной загрузке (т.е.
jdk\jre\lib\rt.jar
). Если найдено, соответствующий файл.class
загружается - Если нет, то первоначальный загрузчик классов делегирует запрос загрузчику расширений. Это будет искать класс по пути
jdk\jre\lib\ext
. Если найдено, соответствующий файл.class
загружается - Если нет, загрузчик классов расширения делегирует запрос загрузчику классов приложения и будет искать класс в пути к классам приложения. Если он найден, он загружается, иначе разработчики получат
ClassNotFoundException
во время выполнения.
- Всякий раз, когда виртуальная машина сталкивается с классом, JVM проверяет, загружен ли указанный файл
- Видимость : в соответствии с этим:
- Загрузчик классов приложения может видеть классы, загруженные родительскими загрузчиками классов, но не соответствует действительности, т. Е. Если класс загружается системным загрузчиком классов, а затем снова при попытке явно загрузить тот же класс с использованием расширения загрузчика классов, возникнет
ClassNotFoundException
в во время выполнения. Например:12345// Printing the classloader of this class.
System.out.println(
"Test.getClass().getClassLoader()?= "
+ Test.
class
.getClassLoader());
// Trying to explicitly load the class again using the extension classloader.
Class.forName(
"com.jcg.classloading.test.Test"
,
true
, Test.
class
.getClassLoader().getParent());
- Загрузчик классов приложения может видеть классы, загруженные родительскими загрузчиками классов, но не соответствует действительности, т. Е. Если класс загружается системным загрузчиком классов, а затем снова при попытке явно загрузить тот же класс с использованием расширения загрузчика классов, возникнет
- Уникальность : в соответствии с этим:
- Класс, загруженный родительским загрузчиком классов, не должен снова загружаться дочерним загрузчиком классов
1.2.2 Как классы загружаются в Java?
Загрузчики классов являются иерархическими. Самый первый класс в приложении специально загружается с помощью статического метода main()
. Все последующие классы загружаются с помощью методов загрузки статических или динамических классов.
- Статическая загрузка классов : в этом методе классы загружаются статически через
new
оператор - Динамическая загрузка классов : в этом методе классы загружаются программно с помощью
Class.forName()
илиloadClass()
. Разница между ними заключается в том, что первый инициализирует объект после загрузки, а второй только загружает класс, но не инициализирует объект.
1.3 Области данных времени выполнения
Как показано на рис. 5, эта подсистема разделена на пять основных компонентов, т.е.
- Область метода . Этот компонент содержит данные уровня класса каждого файла
.class
такие как метаданные, пул постоянной среды выполнения, статические переменные, код для методов и т. Д. В JVM имеется только одна область метода, которая используется всеми классами. Память, выделенная этой области, по умолчанию выделяется JVM или может быть увеличена в соответствии с потребностями вычислений. Следующее исключительное условие связано с этой областью, т.е.- Если область метода недоступна для удовлетворения запроса выделения памяти, JVM выдает ошибку
OutOfMemory
- Если область метода недоступна для удовлетворения запроса выделения памяти, JVM выдает ошибку
- Область кучи : Этот компонент является частью памяти JVM, где хранятся все объекты и соответствующие им переменные экземпляра и массивы. Эта область памяти создается при запуске JVM, и существует только одна область кучи, совместно используемая несколькими потоками, поскольку данные, хранящиеся в этой области, не являются поточно-ориентированными. Если объекты, хранящиеся в памяти кучи, не имеют ссылки, память для этого объекта восстанавливается сборщиком мусора (то есть автоматической системой управления хранением); объекты в этой области никогда не освобождаются явно. Следующее исключительное условие связано с этой областью, т.е.
- Если для вычисления требуется больше области кучи, чем доступно, JVM выдает ошибку
OutOfMemory
- Если для вычисления требуется больше области кучи, чем доступно, JVM выдает ошибку
- Область стека : этот компонент снова является частью памяти JVM, где хранятся все временные переменные. Эта область имеет кадры стека и выделяет один кадр для каждого потока. Когда выполнение потока завершено, этот кадр также уничтожается. Область стека является поточно-ориентированной, так как она не является общим ресурсом и разделена на три дочерних объекта, таких как:
- Массив локальных переменных: эти локальные переменные используются виртуальной машиной для передачи параметров при вызове метода
- Стек операндов: это место является фактическим местом для выполнения операций во время выполнения метода. Виртуальная машина берет операнд из стека, оперирует с ним и помещает результат обратно в стек операндов. Этот стек также подготавливает аргументы для передачи в метод и даже для получения результатов метода
- Данные кадра
Следующее исключительное условие связано с этой областью, т.е.
- Если для обработки потока требуется стек виртуальной машины, превышающий допустимый предел, JVM выдает ошибку
StackOverflow
- Если стек виртуальной машины недостаточно расширен, JVM выдает ошибку
OutOfMemory
- Регистры ПК (счетчик программ) : этот компонент содержит адрес инструкции JVM, которая в данный момент выполняется. Каждый поток в Java имеет свой собственный регистр ПК для хранения адреса выполняемой в данный момент инструкции
- Стеки собственного метода : этот компонент написан на другом языке и содержит информацию о собственном методе. Каждый поток в Java имеет отдельный стек собственных методов. Следующее исключительное условие связано с этой областью, т.е.
- Если для обработки потока требуется собственный стек, превышающий допустимый предел, JVM выдает ошибку
StackOverflow
- Если собственный стек методов недостаточно расширен, JVM выдает ошибку
OutOfMemory
- Если для обработки потока требуется собственный стек, превышающий допустимый предел, JVM выдает ошибку
Вот и все для этого поста. Счастливого обучения!
2. Вывод
В этом руководстве разработчики рассмотрели компоненты ClassLoader виртуальной машины и области данных времени выполнения. Вы можете скачать образец кода в разделе « Загрузки ».
3. Загрузите исходный код
Это было учебное пособие по компонентам ClassLoader и Runtime Data Areas виртуальной машины.