Как работает JVM - Архитектура JVM
JVM (Java Virtual Machine) выступает в качестве среды выполнения для запуска Java-приложений. Именно JVM вызывает метод main, присутствующий в java-коде. JVM является частью JRE (Java Runtime Environment).
Java-приложения называются WORA (Write Once Run Anywhere). Это означает, что программист может написать Java-код на одной системе и быть уверен в том, что он будет работать на любой другой системе с поддержкой Java без каких-либо изменений. Это возможно благодаря JVM.
<-— Cодержание со всеми статьями цикла по Java
Оригинальная статья на английском
Когда мы отдаем компилятору файл .java, он создаст файл .class (содержащий байт-код) с теми же именами классов, которые присутствуют в файле .java. Этот файл .class проходит различные этапы во его время запуска. Эти этапы и составляют всю JVM.
Class Loader
В основном он отвечает за три действия.
Загрузка
Загрузчик классов считывает файл ".class", генерирует соответствующие двоичные данные и сохраняет их в method area (область методов). Для каждого файла ".class" JVM сохраняет в области методов следующую информацию:
- имя загруженного класса и его непосредственного родительского класса.
- к чему относиться ".class": к классу, интерфейсу или Enum.
- информация о модификаторе, переменных и методах и т.д.
После загрузки файла ".class" JVM создает объект типа Class для представления его heap. Обратите внимание, что этот объект имеет тип Class, предопределенный в пакете java.lang. Этот объект Class может быть использован для получения информации на уровне класса, такой как имя класса, имя родителя, информация о методах и переменных и т.д. Чтобы получить ссылку на этот объект, мы можем использовать метод getClass() класса Object.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Student student = new Student();
Class classStudent = student.getClass();
//получить имя класса
System.out.println(classStudent.getName());
//получить массив всех методов
Method allMethods[] = classStudent.getDeclaredMethods();
for (Method method : allMethods) {
System.out.println(method.getName());
} //получить массив всех полей
Field allField[] = classStudent.getDeclaredFields();
for (Field field : allField) {
System.out.println(field.getName());
}
}
}
/*Пример класса, информация о котором
извлекается выше с помощью объекта Class*/
class Student {
private String name;
private int roll_No;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRoll_no() {
return roll_No;
}
public void setRoll_no(int roll_no) {
this.roll_No = roll_no;
}
}Student getName setName getRoll_no setRoll_no name roll_No
Примечание: Для каждого загруженного файла ".class" создается только один объект класса.
Student student2 = new Student(); Class student2 = student2.getClass(); System.out.println(student==student2); // true
Связывание
Выполняет verification, preparation и ( при необходимости) resolution.
- Verification: Обеспечивает корректность файла .class, т.е. проверяет, правильно ли отформатирован этот файл и сгенерирован ли он подходящим компилятором или нет. Если верификация не удалась, мы получаем исключение java.lang.VerifyError. Эта работа выполняется компонентом ByteCodeVerifier. После завершения этой операции файл класса готов к компиляции.
- Preparation: JVM выделяет оперативную память для переменных класса и инициализирует память до значений по умолчанию.
- Resolution: Это процесс замены symbolic references (символьных ссылок) типа на direct references (прямые ссылки). Другими словами Resolution - это нахождение конечного значения символьной ссылки, т.е. замена ее на жесткую ссылку, указывающую сразу на конечный объект. Это делается путем поиска в method area, чтобы найти объект, на который имеется ссылка.
Cимвольная ссылка - это ссылка, которая указывает ни на конкретный объект, а на еще одну ссылку (а та, возможно, на еще одну), которая указывает непосредственно на объект.
Прямая ссылка - указание непосредственно на сам объект.
Инициализация
На этом этапе всем статическим переменным присваиваются значения, определенные в коде и статическом блоке (если есть). Это выполняется сверху вниз в классе и от родителя к потомку в иерархии классов.
В общем, есть три загрузчика классов:
- Bootstrap ClassLoader: базовый загрузчик, также называется Primordial ClassLoader. Загружает стандартные классы JDK из архива rt.jar
- Extension ClassLoader – загрузчик расширений.
- System/Application ClassLoader – системный загрузчик. Загружает классы приложения, определенные в переменной среды окружения CLASSPATH
public class Test {
public static void main(String[] args) {
/*Класс String загружается загрузчиком bootstrap, и bootstrap loader
не является объектом Java, следовательно, null*/ System.out.println(String.class.getClassLoader());
/*Класс Test загружается System ClassLoader*/ System.out.println(Test.class.getClassLoader());
}
}null jdk.internal.loader.ClassLoaders$AppClassLoader@8bcc55f
Примечание: JVM следует принципу делегирования-иерархии для загрузки классов. System ClassLoader передает запрос на загрузку Extension ClassLoader, а загрузчик классов расширения передает запрос загрузчику классов bootstrap. Если класс найден в пути boot-strap, он загружается. Иначе запрос снова передается загрузчику Extension ClassLoader, а затем System ClassLoader. Наконец, если System ClassLoader не смог загрузить класс, то мы получаем исключение ClassNotFoundException.
JVM memory
- Method area: в области метода хранится вся информация про класс - имя класса, имя непосредственного родительского класса, информация о методах и переменных и т. д., включая статические переменные. На каждую JVM существует только одна область методов.
- Heap area: информация обо всех объектах хранится в Heap area. На каждую JVM приходится одна Heap area. Она также является общим ресурсом.
- Stack area: для каждого потока JVM создает уникальный стек, который хранится в stack area. Каждый блок этого стека называется записью активации(кадром стека), в котором хранятся вызовы методов. Все локальные переменные этого метода хранятся в соответствующем фрейме. После завершения потока его стек времени выполнения будет уничтожен JVM. Это не общий ресурс.
- PC Registers: счетчик команд нашего потока. Хранит в себе адрес выполняемой инструкции. Каждый поток имеет собственные регистры.
- Native method stacks: для каждого потока создается отдельный стек. В нем хранится информация о нативных методах.
Execution Engine
Механизм обработки ".class" (байт-код). Он считывает байт-код построчно, используя данные и информацию, находящиеся в различных областях памяти, и выполняет инструкции. Его можно разделить на три части:
- Интерпретатор: Он интерпретирует байт-код строка за строкой и затем выполняет. Недостатком здесь является то, что когда один метод вызывается несколько раз, каждый раз требуется интерпретация.
- Компилятор Just-In-Time Compiler(JIT) : используется для повышения эффективности интерпретатора. Он компилирует весь байткод и заменяет его на нативный код. Поэтому всякий раз, когда интерпретатор видит повторяющиеся вызовы методов, JIT предоставляет нативный код для этой части. Из-за этого повторная интерпретация не требуется, что повышает эффективность.
- Garbage Collector: Уничтожает объекты без ссылок. Подробнее о сборщике мусора читайте в статье Сборщик мусора.
Java Native Interface (JNI)
Это интерфейс взаимодействия с нативными библиотеками методов. Предоставляет нативные библиотеки (C, C++), необходимые для выполнения кода. Он позволяет JVM вызывать библиотеки C/C++ и быть вызванным библиотеками C/C++, которые могут быть специфичны для аппаратного обеспечения.
Native Method Libraries
Это коллекция нативных библиотек (C, C++), которые требуются Java Native Interface (JNI).
<-— Cодержание со всеми статьями цикла по Java