November 22, 2021

Как работает 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

  1. Method area: в области метода хранится вся информация про класс - имя класса, имя непосредственного родительского класса, информация о методах и переменных и т. д., включая статические переменные. На каждую JVM существует только одна область методов.
  2. Heap area: информация обо всех объектах хранится в Heap area. На каждую JVM приходится одна Heap area. Она также является общим ресурсом.
  3. Stack area: для каждого потока JVM создает уникальный стек, который хранится в stack area. Каждый блок этого стека называется записью активации(кадром стека), в котором хранятся вызовы методов. Все локальные переменные этого метода хранятся в соответствующем фрейме. После завершения потока его стек времени выполнения будет уничтожен JVM. Это не общий ресурс.
  4. PC Registers: счетчик команд нашего потока. Хранит в себе адрес выполняемой инструкции. Каждый поток имеет собственные регистры.
  5. 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

Оригинальная статья на английском