18 KiB
JVM (Java Virtual Machine)
Виртуальная машина Java (JVM) - это механизм, обеспечивающий среду выполнения для управления Java-кодом или приложениями. Виртуальная машина является независимой оболочкой исполнения кода, благодаря которой возможен запуск на любой ОС, без влияния ОС на выполняемую программу.
За что отвечает JVM:
- Загрузка, проверка (верификация) и исполнение байт кода;
- Предоставление среды выполнения для выполнения байт-кода;
- Управление памятью и очисткой мусора (Garbage collection);
JVM работает с 2мя типами данных: примитивные типы (primitive types) и ссылочные типы (reference types).
Примитивы
JVM работает с примитивными значениями (целыми числами и числами с плавающей точкой) и ссылками. По сути, JVM - это 32-битная машина.
Типы long
и double
, которые являются 64-битными, поддерживаются изначально,
но занимают две единицы памяти в frame's local
или стеке операндов, поскольку каждая единица составляет 32 бита.
Типы boolean
, byte
, short
и char
имеют расширенный знак (кроме char
с нулевым расширением) и работают как 32-разрядные целые числа, так же как и типы int
.
Меньшие типы имеют только несколько специфических для типа инструкций для загрузки, хранения и преобразования типов.
boolean
значение работает как 8-битное byte
значения, где 0 представляет значение false, а 1 - значение true.
В JVM есть сборщик мусора (garbage-collector) для хранения объектов и массивов. Код, константы и другие данные класса хранятся в «области метода».
Область метода является логически частью кучи, но реализации могут обрабатывать область метода отдельно от кучи и, например, могут не собирать мусор.
Каждый поток JVM также имеет свой собственный стек вызовов (для ясности называемый «стек виртуальных машин Java»), в котором хранятся frames
.
Новый фрейм (frame
) создается каждый раз, когда вызывается метод, и фрейм уничтожается при выходе из этого метода.
Типы ссылок и значения
Существует три типа ссылочных типов: типы классов, типы массивов и типы интерфейсов. Их значения являются ссылками на динамически создаваемые экземпляры классов, массивы или экземпляры классов или массивы, которые реализуют интерфейсы соответственно.
![JVM Architecture] (jvmarchitecture.png)
1. Classloader (Загрузчик классов)
Java Classloader является частью JRE, которая динамичиски закгружает Java классы в JVM. Обычно классы загружаются только по запросу. Система исполнения в Java не должна знать о файлах и файловых системах благодаря загрузчику классов. Делегирование является важной концепцией, которую выполняет загрузчик. Загрузчик классов отвечает за поиск библиотек, чтение их содержимого и загрузку классов, содержащихся в библиотеках. Эта загрузка обычно выполняется «по требованию», поскольку она не происходит до тех пор, пока программа не вызовет класс. Класс с именем может быть загружен только один раз данным загрузчиком классов.
При запуске JVM, используются три загрузчика классов:
- Bootstrap class loader (Загрузчик класса Bootstrap)
- Extensions class loader (Загрузчик класса расширений)
- System class loader (Системный загрузчик классов)
Загрузчик класса Bootstrap загружает основные библиотеки Java, расположенные в папке <JAVA_HOME>/jre/lib
.
Этот загрузчик является частью ядра JVM, написан на нативном коде.
Загрузчик класса расширений загружает код в каталоги расширений
(<JAVA_HOME>/jre/lib/ext
, или любой другой каталог, указанный системным свойством java.ext.dirs
).
Системный загрузчик загружает код, найденный в java.class.path
, который сопоставляется с переменной среды CLASSPATH
.
Это реализуется классом sun.misc.Launcher$AppClassLoader
.
Пользовательский загрузчик классов
Загрузчик классов написан на Java. Поэтому возможно создать свой собственный загрузчик классов, не понимая тонких деталей JVM. У каждого загрузчика классов Java есть родительский загрузчик классов, определенный при создании экземпляра нового загрузчика классов или в качестве системного загрузчика классов по умолчанию для виртуальной машины.
Что делает возможным следующее:
- загружать или выгружать классы во время выполнения (например, динамически загружать библиотеки во время выполнения, даже из ресурса HTTP).
Это важная особенность для:
- реализация скриптовых языков;
- использование bean builders;
- добавить пользовательскую расширение;
- позволяя нескольким пространствам имен общаться. Например, это одна из основ протоколов CORBA / RMI;
- изменить способ загрузки байт-кода (например, можно использовать зашифрованный байт-код класса Java);
- модифицировать загруженный байт-код (например, для переплетения аспектов во время загрузки при использовании аспектно-ориентированного программирования);
Загрузчик классов выполняет три основных действия в этом строгом порядке:
- Загрузка: находит и импортирует двоичные данные для типа.
- Связывание: выполняет проверку, подготовку и (необязательно) разрешение.
- Проверка: обеспечивает правильность импортируемого типа.
- Подготовка: выделяет память для переменных класса и инициализация памяти значениями по умолчанию.
- Разрешение: преобразует символические ссылки из типа в прямые ссылки.
- Инициализация: вызывает код Java, который инициализирует переменные класса их правильными начальными значениями.
2. Области данных времени выполнения (Run-Time Data Areas)
JVM выделяет множество областей данных во время выполнения, к-рые используются во время выполнения программы. Некоторые участки данных созданы JVM во время старта и уничтожаются во время её выключения. Другие создаются для каждого потока и уничтожаются когда поток уничтожается.
2.1. The pc Register
Виртуальная машина Java может поддерживать много потоков исполнения одновременно. Каждый поток виртуальной машины Java имеет свой собственный регистр PC (programm counter). В любой момент каждый поток виртуальной машины Java выполняет код одного метода, а именно текущий метод для этого потока. Если этот метод не является native, регистр pc содержит адрес инструкции виртуальной машины Java, выполняемой в настоящее время.
2.2. Java Virtual Machine Stacks
Каждый поток виртуальной машины Java имеет собственный стек виртуальной машины Java, созданный одновременно с потоком. Стек виртуальной машины Java хранит frames. Cтеки виртуальных машин Java могут иметь фиксированный размер или динамически расширяться и сжиматься в соответствии с требованиями вычислений.
2.3. Heap
Виртуальная машина Java имеет кучу, которая используется всеми потоками виртуальной машины Java. Куча - это область данных времени выполнения, из которой выделяется память для всех экземпляров и массивов классов. Куча создается при запуске виртуальной машины. Хранилище для объектов восстанавливается автоматической системой управления данными (известной как сборщик мусора); объекты никогда не освобождаются явно. Виртуальная машина Java не предполагает какого-либо конкретного типа системы автоматического управления хранением данных, и метод управления может быть выбран в соответствии с системными требованиями разработчика. Куча может иметь фиксированный размер или может быть расширена в соответствии с требованиями вычислений и может быть сокращена, если большая куча становится ненужной. Память для кучи не должна быть смежной.
2.4. Method Area
Виртуальная машина Java имеет область методов, которая является общей для всех потоков виртуальной машины Java. Область метода аналогична области хранения скомпилированного кода на традиционном языке или аналогична сегменту «текст» в процессе операционной системы. Он хранит структуры для каждого класса, такие как пул постоянных времени выполнения, данные полей и методов, а также код для методов и конструкторов, включая специальные методы, используемые при инициализации классов и экземпляров и инициализации интерфейса.
Хотя область метода является логически частью кучи, простые реализации могут не обрабатываться собиращиком мусора. Область метода может иметь фиксированный размер или может быть расширена в соответствии с требованиями вычислений и может быть сокращена, если большая область метода становится ненужной.
2.5. Run-Time Constant Pool
A run-time constant pool существует для каждого класса или интерфейса в рантайме и представленно constant_pool таблицей в *.class файле. Он содержит несколько видов констант: от числовых литералов, известных во время компиляции, до ссылок на методы и поля, которые должны быть разрешены во время выполнения. Сам run-time constant pool выполняет функцию, аналогичную функции таблицы символов для обычного языка программирования, хотя он содержит более широкий диапазон данных, чем типичная таблица символов. Каждый run-time constant pool отделён от JVM's method area. JVM создаёт run-time constant pool вместе с созданием class или interface.
2.6. Native Method Stacks
Реализация виртуальной машины Java может использовать обычные стеки, обычно называемые «стеки С», для поддержки native methods (методов, написанных на языке, отличном от языка программирования Java).
3. Frames
Frame используется для хранения данных и частичных результатов, а также для выполнения динамического связывания, возврата значений для методов и отправки исключений. Новый frame создается каждый раз, когда вызывается метод. Frame уничтожается, когда завершается его вызов метода, является ли это завершение нормальным или резким (он генерирует неперехваченное исключение). Frames выделяются из стека потока, создающего frame. Каждый frame имеет свой собственный массив локальных переменных, свой собственный стек операндов и ссылку на пул констант во время выполнения класса текущего метода. Размеры массива локальных переменных и стека операндов определяются во время компиляции и предоставляются вместе с кодом для метода, связанного с фреймом. Таким образом, размер структуры данных frame-а зависит только от реализации виртуальной машины Java, и память для этих структур может быть выделена одновременно при вызове метода.
Только один frame, frame для метода выполнения, активен в любой точке данного потока управления. Этот кадр называется текущим frame, а его метод известен как текущий метод. Класс, в котором определен текущий метод, является текущим классом. Операции над локальными переменными и стеком операндов обычно выполняются со ссылкой на текущий frame.
Frame перестает быть текущим, если его метод вызывает другой метод или если его метод завершается. Когда метод вызывается, новый frame создается и становится текущим, когда управление переходит к новому методу. При возврате метода текущий frame передает результат вызова метода, если таковой имеется, в предыдущий frame. Текущий frame затем отбрасывается, так как предыдущий frame становится текущим. Обратите внимание, что frame, созданный потоком, является локальным для этого потока и на него не может ссылаться ни один другой поток.