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

Как мы уже знаем, весь исходный код, написанный на языке программирования Java, сначала компилируется в байт-коды (инструкции для виртуальной машины Java) с помощью компилятора javac, входящего в состав Java Development Kit. Байт-коды сохраняются в двоичном формате файла, который называется форматом файла class. Эти файлы классов (байт-коды) затем загружаются динамически (только при необходимости) в память компонентом загрузчика классов виртуальной машины Java. В режиме простого интерпретатора механизм выполнения Java выполняет эти байт-коды один за другим на ЦП хост-машины.

Каждый файл с расширением .java компилируется как минимум в один файл .class. Для каждого класса, интерфейса и модуля, определенного в исходном коде, существует один файл .class. Это также относится к вложенным классам или интерфейсам.

Примечание. Для простоты файлы с расширением .class здесь называются файлом класса.

Напишем простую программу

Запуск javac для этого файла приводит к созданию следующих файлов.

ClassOne$StaticNestedClass.class
ClassOne.class
ClassTwo.class
InterfaceOne.class

Как видите, для каждого класса создается один файл класса, интерфейс которого определен в исходном файле.

Что внутри файла класса?

Файл класса - это двоичный формат файла. Информация обычно записывается в файл класса без пробелов или отступов между последовательными частями информации, все выравнивается по границам байтов. Все 16-битные и 32-битные величины создаются путем чтения двух и четырех последовательных 8-битных байтов соответственно.

Файл класса содержит следующую информацию

Магическое число: первые четыре байта каждого файла класса всегда равны 0xCAFEBABE. Эти четыре байта идентифицируют формат файла класса от других.

Основная и дополнительная версия: вторые четыре байта файла класса содержат номера основной и дополнительной версии. Вместе старший и дополнительный номер версии определяют версию формата файла класса. Если файл класса имеет основной номер версии M и дополнительный номер версии m, мы обозначаем версию его формата файла класса как M.m.

Каждая JVM имеет максимальную версию, которую она может загрузить, и JVM отклоняют файлы классов с более поздними версиями. Например, Java 11 поддерживает основные версии от 45 до 55, а Java 12 поддерживает основные версии 45..56.

Пул констант: таблица структур (разнородных), представляющая строковые константы, имена классов и интерфейсов, имена полей, имена методов и другие константы, которые упоминаются в структуре ClassFile и его подструктуры. Каждый элемент этого пула констант начинается с однобайтового тега, определяющего тип константы в этой позиции в таблице. В зависимости от типа константы следующие байты могут быть постоянным значением или ссылкой на другой элемент в пуле.

Флаги доступа: список флагов, который сообщает, имеет ли этот класс или интерфейс открытый или частный доступ, является ли этот класс окончательным или разрешающим расширения. Различные флаги, такие как ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, ACC_ENUM и т. Д., Определены в документе спецификации JVM.

Этот класс: относится к записи в постоянный пул.

Суперкласс: относится к записи в постоянный пул.

Интерфейсы: количество интерфейсов, реализованных этим классом.

Количество полей: количество полей в этом классе или интерфейсе.

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

Счетчик методов: количество методов в классе или интерфейсе. Счетчик включает только те методы, которые явно определены этим классом, но не любые методы, которые могут быть унаследованы. из суперклассов.

Методы. За счетчиком методов следуют сами методы. Структура каждого метода содержит несколько частей информации о методе, включая дескриптор метода (его тип возвращаемого значения и список аргументов). , количество слов, необходимых для локальных переменных метода, максимальное количество слов стека, необходимое для стека операндов метода, таблица исключений, перехваченных методом, последовательность байт-кода и таблица номеров строк.

Счетчик атрибутов: количество атрибутов в этом классе, интерфейсе или модуле.,

Атрибуты. За счетчиком атрибутов следует таблица или структуры переменной длины, описывающие каждый атрибут. Например, один атрибут - это атрибут исходного кода; он показывает имя исходного файла, из которого был скомпилирован этот файл класса.

Хотя формат файла класса нельзя прочитать напрямую, JDK предоставляет инструмент под названием javap, который скрывает файл класса и выводит его содержимое в удобочитаемом формате.

Давайте напишем простую программу на Java, как указано ниже.

Давайте скомпилируем эту программу с помощью команды javac, которая создаст файл HelloWorld.class, а затем воспользуемся инструментом javap, чтобы скрыть этот файл HelloWorld.class. Использование javap с -v (подробный) в HelloWorld.class дает следующий результат

Здесь вы можете видеть, что этот класс общедоступен, его постоянный пул содержит 37 записей, имеет один атрибут (SourceFile внизу), реализует два интерфейса (Serializable, Cloneable), имеет нулевые поля и 2 метода.

Вам может быть интересно, что в исходном коде есть только один статический основной метод, но в файле класса указано, что существует два метода. Что ж, помните конструктор по умолчанию в языке программирования Java, это конструктор без аргументов, добавленный компилятором javac, чьи байт-коды также видны в выводе. Конструкторы рассматриваются как методы.

Подробнее об инструменте javap можно прочитать здесь.

Совет: вы можете использовать инструмент javap, чтобы увидеть, чем лямбда-выражения отличаются от анонимных внутренних классов.

В следующей части этой серии я расскажу о структуре памяти запущенного экземпляра JVM.

Другие полезные ресурсы для изучения Java вам могут понравиться
5 лучших курсов для изучения внутреннего устройства JVM
10 бесплатных курсов для изучения Java с нуля
10 книг для углубленного изучения Java
10 инструментов, которые должен знать каждый Java-разработчик
10 причин изучать языки программирования Java
10 фреймворков, которые Java и веб-разработчики должны изучить в 2019 году
10 советов, как стать лучшим Java-разработчиком в 2019 году
5 лучших Java-фреймворков, которые следует изучить в 2019 году
10 библиотек тестирования, которые должен знать каждый Java-разработчик