Друзья, изучающие Java, должны знать, что Java с самого начала использует лозунг независимости от платформы, говоря: «Напиши один раз, работай где угодно». На самом деле, когда дело доходит до неуместности, у платформы Java есть еще одна неуместность — независимость от языка. , для достижения языковой независимости, то файловая структура класса в системе Java или. Очень важно сказать, что это байт-код. Фактически, с самого начала в Java было два набора спецификаций: один — спецификация языка Java, а другой — спецификация виртуальной машины Java. Спецификация языка Java предусматривает только ограничения. связанные с языком Java и правилами, а спецификации виртуальных машин действительно разработаны с учетом кроссплатформенности. Сегодня мы рассмотрим практический пример, чтобы увидеть, как должен выглядеть байт-код, соответствующий файлу класса в Java. В этой статье сначала будет объяснено в общих чертах, из чего состоит класс контента, а затем будет использован реальный класс Java для анализа файловой структуры класса.
Прежде чем продолжить, нам сначала необходимо прояснить следующие моменты:
1) Файлы классов состоят из 8-байтовых потоков байтов. Эти потоки байтов расположены строго в указанном порядке, и между байтами нет промежутков. Для файлов, превышающих 8 байт, данные будут храниться в порядке Big-Endian. то есть старший байт хранится по младшему адресу, а младший байт хранится по старшему адресу. Это также является ключом к кроссплатформенным файлам классов, поскольку в архитектуре PowerPC используется обратный порядок хранения, а в процессорах серии x86 используется прямой порядок хранения, поэтому для того, чтобы файлы классов поддерживались в каждой архитектуре процессора, унифицированное хранилище. В порядке спецификации виртуальных машин должны быть унифицированы.
2) Структура файла класса использует структуру, подобную языку C, для хранения данных. Существует два основных типа элементов данных: беззнаковые числа и таблицы. Беззнаковые числа используются для выражения чисел, ссылок на индексы и строк, таких как u1, u2. , u4 и u8 представляют собой 1 байт, 2 байта, 4 байта и 8 байтов беззнаковых чисел соответственно, а таблица представляет собой составную структуру, состоящую из нескольких беззнаковых чисел и других таблиц. Возможно, всем здесь не очень понятно, что такое беззнаковые числа и таблицы, но это не важно, когда я буду приводить примеры ниже.
После пояснения двух вышеуказанных моментов давайте взглянем на конкретные данные, содержащиеся в потоке байтов, упорядоченном в строгом порядке в файле классов:
Глядя на изображение выше, нам нужно обратить внимание на одну вещь, например, cp_info, cp_info представляет пул констант. На рисунке выше константа_pool[constant_pool_count-1] используется для представления пула констант с константой_pool_co. unt-1, здесь он выражен в виде массива, но не думайте ошибочно, что константные длины всех константных пулов одинаковы. На самом деле здесь используется метод массива просто для удобства описания, но. это не так. В языках программирования массив типа int имеет одинаковую длину каждого int. После прояснения этого момента давайте оглянемся назад и посмотрим, что конкретно представляет каждый элемент на рисунке выше.
1) u4 Magic представляет собой магическое число, а магическое число занимает 4 байта. Что делает магическое число? На самом деле это означает, что тип файла — это файл класса, а не изображение JPG или фильм AVI. Магическое число, соответствующее файлу класса, — 0xCAFEBABE.
2) u2 major_version представляет дополнительный номер версии файла класса, и этот номер версии представляет собой беззнаковое числовое представление типа u2.
3) u2 major_version представляет собой основной номер версии файла класса, а основной номер версии представляет собой беззнаковое числовое представление типа u2. Major_version и Minor_version в основном используются для указания, принимает ли текущая виртуальная машина текущую версию файла класса. Версии файлов классов, скомпилированные разными версиями компиляторов Java, различаются. Более поздняя версия виртуальной машины поддерживает файловую структуру классов, скомпилированную более ранней версией компилятора. Например, виртуальная машина, соответствующая Java SE 6.0, поддерживает файловую структуру классов, скомпилированную компилятором Java SE 5.0, но не наоборот.
4) u2 Constant_pool_count представляет количество постоянных пулов. Здесь нам нужно сосредоточиться на том, что такое пул констант. Не путайте его с пулом констант времени выполнения в модели памяти Jvm. Пул констант в файле классов в основном хранит литералы и ссылки на символы, где литералы в основном включают строки. значение конечной константы или Или начальное значение определенного атрибута и т. д., тогда как ссылки на символы в основном хранят полные имена классов и интерфейсов, имена полей и дескрипторов, имена методов и дескрипторов. Имена здесь могут быть понятны каждому, что касается. Концепция дескрипторов, мы поговорим о ней позже, когда будем обсуждать таблицу полей и таблицу методов. Кроме того, всем известно, что модель памяти Jvm состоит из кучи, стека, области методов и счетчика программ, а в области методов есть область, называемая пулом констант времени. Вещи, хранящиеся в пуле констант времени выполнения, на самом деле являются объектами. бессмертие компилятора. Различные литералы и ссылки на символы, но пул констант времени выполнения является динамическим. Во время выполнения к нему можно добавлять другие константы. Наиболее представительным является внутренний метод String.
5) cp_info представляет пул констант, который содержит различные литералы и ссылки на символы, упомянутые выше. Всего в пул констант в Спецификации виртуальной машины Java Java SE 7 Edition помещено в общей сложности 14 элементов. Каждая константа представляет собой таблицу, и каждая константа использует общий частичный тег для указания типа константы.
Конкретные детали кратко описаны ниже, и мы уточним их в последующих примерах.
Флаг тега CONSTANT_Utf8_info равен 1, флаг тега CONSTANT_Integer_info строки в кодировке UTF-8 равен 3, целочисленный литерал тега CONSTANT_Float_info равен 4, флаг тега CONSTANT_Long_info с плавающей запятой равен 5, длинный целочисленный литерал тега CONSTANT_Double_info. Бит равен 6, флаг тега литерала двойной точности CONSTANT_Class_info равен 7, Символическая ссылка тега CONSTANT_String_info класса или интерфейса равна 8, литеральная ссылка тега CONSTANT_Fieldref_info строкового типа равна 9, символическая ссылка тега поля CONSTANT_Methodref_info равна 10, символическая ссылка метода в теге класса CONSTANT_InterfaceMethodref_info равна 11, символическая ссылка на метод в теге интерфейса CONSTANT_NameAndType_info Бит флага 12, имена полей и методов, а также символические ссылки на типы.
6) u2 access_flags представляет информацию о доступе к классу или интерфейсу, как показано на следующем рисунке:
7) u2 this_class представляет индекс пула констант класса, указывая на константу CONSTANT_Class_info в пуле констант.
8) u2 super_class представляет индекс суперкласса, указывающий на константу CONSTANT_Class_info в пуле констант.
9) u2 Interface_counts представляет количество интерфейсов.
10) u2 интерфейс[interface_counts] представляет таблицу интерфейсов, каждый элемент в ней указывает на константу CONSTANT_Class_info в пуле констант
11) u2 columns_count представляет количество переменных экземпляра и переменных класса класса.
12) field_info поля[fields_count] представляет информацию таблицы полей, где структура таблицы полей показана ниже:
На приведенном выше рисунке access_flags представляет представление доступа к полю. Например, поле является общедоступным, частным и защищенным. и т. д., name_index представляет имя поля, указывающее на константу типа CONSTANT_UTF8_info в пуле констант, descriptor_index представляет дескриптор поля, который также указывает на константу типа CONSTANT_UTF8_info в пуле констант, а атрибуты_count представляют количество таблиц атрибутов в таблице полей и таблице атрибутов. Это расширяемая структура, используемая для описания полей, методов и атрибутов классов. Различные версии виртуальной машины Java поддерживают различное количество таблиц атрибутов.
13) u2 Methods_count представляет количество таблиц методов.
14) Method_info представляет таблицу методов. Конкретная структура таблицы методов показана на рисунке ниже:
Среди них access_flags представляет представление доступа к методу, name_index представляет индекс имени, descriptor_index представляет дескриптор метода, а атрибуты_count и атрибут_info аналогичны таблицам атрибутов в таблице полей, за исключением того, что атрибуты в таблице атрибутов в таблице полей и таблице методов различаются, например, атрибут «Код» в таблице методов представляет код метода, но в таблице полей нет атрибута «Код». Сколько атрибутов содержится в конкретном классе, мы обсудим позже, когда мы посмотрим на таблицу атрибутов в файловой структуре класса.
15) Attribute_count представляет количество таблиц атрибутов. Когда речь идет о таблицах атрибутов, нам необходимо уточнить следующие моменты:
Таблица атрибутов находится в конце структуры файла классов, в таблице полей, таблице методов и атрибуте кода. То есть таблица атрибутов также может существовать в таблице атрибутов. Длина таблицы атрибутов не фиксирована. Различные атрибуты имеют разную длину.
После описания состава каждого элемента в файловой структуре классов выше мы используем практический пример для объяснения следующего содержания.
Скопируйте код кода следующим образом:
пакет com.ejushang.TestClass;
публичный класс TestClass реализует Super{
частный статический окончательный int staticVar = 0;
частный int экземплярVar = 0;
public int instanceMethod (int param) {
вернуть параметр+1;
}
}
интерфейс Супер{ }
Бинарная структура TestClass.class, соответствующая TestClass.java, скомпилированная с помощью javac jdk1.6.0_37, показана на рисунке ниже:
Далее мы проанализируем поток байтов, показанный на рисунке выше, на основе файловой структуры класса, упомянутой ранее.
1) Магическое число <br/>Из файловой структуры класса мы знаем, что первые 4 байта — это магическое число. На рисунке выше содержимое адреса 00000000h-00000003h является магическим числом. мы можем узнать магический номер файла класса. Это число — 0xCAFEBABE.
2) Основные и дополнительные номера версий <br/>Следующие 4 байта — это основные и дополнительные номера версий. На рисунке выше мы видим, что соответствующие числа от 00000004h до 00000005h равны 0×0000, поэтому младшая_версия класса. имеет размер 0×0000, а соответствующее содержимое 00000006h-00000007h равно 0×0032, поэтому версия major_version файла класса равна 0×0032, что в точности является основной и второстепенной версией, соответствующей классу, скомпилированному jdk1.6.0 без целевой параметр.
3) Номер пула констант <br/>Следующие 2 байта представляют номер пула констант от 00000008h до 00000009h. Из рисунка выше мы можем знать, что его значение равно 0×0018, что равно 24 в десятичном формате, но для Необходимо уточнить количество пулов констант. Число пулов констант равно Constant_pool_count-1. Причина, по которой оно уменьшается на единицу, заключается в том, что индекс 0 означает, что элементы данных в классе не ссылаются ни на какие константы в пуле констант.
4) Пул констант <br/>Выше мы говорили, что в пуле констант существуют разные типы констант. Давайте посмотрим на первую константу TestClass.class. Мы знаем, что каждая константа представлена идентификатором тега типа u1. Тип константы, 0000000ah на рисунке выше. Содержимое — 0x0A, преобразованное во вторичную систему — 10. Из приведенного выше описания типа константы видно, что константа с тегом 10 — это Constant_Methodref_info, а структура Constant_Methodref_info показана на рисунке ниже:
Среди них class_index указывает на константу типа CONSTANT_Class_info в пуле констант. Из структуры двоичного файла TestClass видно, что значение class_index равно 0×0004 (адрес 0000000bh-0000000ch), что означает, что оно указывает на. четвертая константа.
name_and_type_index указывает на константу типа CONSTANT_NameAndType_info в пуле констант. Как видно из рисунка выше, значение name_and_type_index равно 0×0013, что означает, что оно указывает на 19-ю константу в пуле констант.
Далее вы можете использовать тот же метод, чтобы найти все константы в пуле констант. Однако JDK предоставляет удобный инструмент, позволяющий просматривать константы, содержащиеся в пуле констант. Вы можете получить все константы в пуле констант с помощью javap -verbose TestClass. Скриншот выглядит следующим образом:
На рисунке выше мы ясно видим, что в пуле констант TestClass имеется 24 константы. Не забудьте 0-ю константу, поскольку 0-я константа используется для указания того, что элементы данных в классе не ссылаются ни на какие константы в классе. постоянный бассейн. Из приведенного выше анализа мы знаем, что первый метод представления констант TestClass заключается в том, что четвертая константа, на которую указывает class_index, — это java/lang/Object, а 19-е значение константы, на которое указывает name_and_type_index, — это <init>:()V From It can. Здесь видно, что первая константа, представляющая метод, представляет собой метод конструктора экземпляра, сгенерированный компилятором Java. Другие константы в пуле констант можно анализировать таким же образом. Хорошо, после анализа пула констант давайте теперь проанализируем access_flags.
5) u2 access_flags представляет информацию о доступе к классам или интерфейсам. Например, Class указывает, является ли это классом или интерфейсом, является ли он общедоступным, статическим, окончательным и т. д. Значение специального флага доступа было упомянуто ранее. Давайте посмотрим на флаг доступа TestClass. Флаг доступа класса — 0000010dh-0000010e, а значение — 0×0021. Согласно битам флагов различных флагов доступа, упомянутых ранее, мы можем знать: 0×0021=0×0001|0×0020, то есть: ACC_PUBLIC и ACC_SUPER имеют значения True, ACC_PUBLIC легко понять, а ACC_SUPER — это флаг, который будет содержаться в классах, скомпилированных после jdk1.2.
6) u2 this_class представляет значение индекса класса, которое используется для представления полного имени класса. Значение индекса класса показано на рисунке ниже:
Как ясно видно на рисунке выше, значение индекса класса равно 0×0003, что соответствует третьей константе пула констант. Благодаря результату javap мы знаем, что третья константа является константой типа CONSTANT_Class_info. где мы можем узнать полную информацию о классе. Полное имя: com/ejushang/TestClass/TestClass.
7) u2 super_class представляет значение индекса родительского класса текущего класса. Значение индекса указывает на константу типа CONSTANT_Class_info в пуле констант. Значение индекса родительского класса показано на рисунке ниже. 0×0004. Проверьте первые четыре константы пула констант. Видно, что полное имя родительского класса TestClass: java/lang/Object.
8) интерфейсы_count и интерфейсы[interfaces_count] представляют количество интерфейсов и каждый конкретный интерфейс. Количество интерфейсов и интерфейсов TestClass показано на рисунке ниже, где 0×0001 означает, что количество интерфейсов равно 1, а 0×. 0005 означает индекс интерфейса в значении пула констант. Найдите пятую константу в пуле констант, ее тип — CONSTANT_Class_info, а ее значение: com/ejushang/TestClass/Super.
9) columns_count и field_info , columns_count представляет количество таблиц field_info в классе, а field_info представляет переменные экземпляра и переменные класса класса. Здесь следует отметить, что field_info не включает поля, унаследованные от родительского класса. field_info имеет такой вид, как показано на рисунке ниже:
Среди них access_flags представляет флаг доступа к полю, например общедоступный, частный, защищенный, статический, окончательный и т. д. Значение access_flags показано на рисунке ниже:
Среди них name_index и descriptor_index являются индексными значениями пула констант, которые соответственно представляют имя поля и дескриптор поля. Имя поля легко понять, но как понять дескриптор поля. поле? Фактически в спецификации JVM дескрипторы полей указаны так, как показано на следующем рисунке:
Среди них всем следует обратить внимание на последнюю строку на рисунке выше. Она представляет собой дескриптор одномерного массива. Дескриптор для String[][] будет [[ Ljava/lang/String, а описание для него. int[][] Символ — [[I. Следующие атрибуты_count и атрибуты_info представляют количество таблиц атрибутов и таблиц атрибутов соответственно. Давайте возьмем приведенный выше TestClass в качестве примера и посмотрим на таблицу полей TestClass.
Во-первых, давайте посмотрим на количество полей. Количество полей в TestClass показано на рисунке ниже:
Как видно на рисунке выше, TestClass имеет два поля. Глядя на исходный код TestClass, мы видим, что полей действительно только два. Далее давайте посмотрим на первое поле. Мы знаем, что первое поле должно быть. Private int staticVar, двоичное представление которого в файле класса показано ниже:
Среди них 0x001A представляет флаг доступа. Глядя на таблицу access_flags, мы можем узнать, что это ACC_PRIVATE, ACC_STATIC, ACC_FINAL. Далее, 0×0006 и 0×0007 представляют собой 6-ю и 7-ю константы в пуле констант соответственно. глядя на пул констант, мы можем узнать, что их значения: staticVar и I, где staticVar — это имя поля, а I — дескриптор поля. В приведенном выше объяснении дескрипторов я описываю переменную типа int. Далее, 0×0001 представляет количество таблиц атрибутов в таблице полей staticVar. Из приведенного выше рисунка видно, что соответствует одна таблица атрибутов. в поле staticVar. 0×0008 представляет восьмую константу в пуле констант. Глядя на пул констант, вы можете видеть, что этот атрибут является атрибутом ConstantValue, а формат атрибута ConstantValue показан на рисунке ниже:
Среди них атрибут имя_индекса выражает индекс пула констант для имени атрибута. В этом примере это константное значение. Длина атрибута константного значения имеет фиксированную длину, равную 2, а константное значение_индекс представляет ссылку в пуле констант. В этом примере оно равно 0. ×0009. Вы можете просмотреть девятую константу. Знаете, она представляет собой константу типа CONSTANT_Integer_info, значение которой равно 0.
Сказав, что частный статический окончательный int staticVar=0, давайте поговорим о частном int экземпляреVar=0 класса TestClass. В этом примере двоичное представление экземпляра экземпляра показано на рисунке ниже:
Среди них 0×0002 означает, что метка доступа — ACC_PRIVATE, 0x000A означает имя поля, которое указывает на 10-ю константу в пуле констант. Глядя на пул констант, вы можете узнать, что имя поля — instanceVar, а 0× 0007 представляет собой дескриптор поля, указывающий на 7-ю константу в пуле констант. Глядя на пул констант, вы можете узнать, что 7-я константа — это I, которая представляет тип экземпляра. Наконец, 0×0000 представляет собой. количество атрибутивных таблиц равно 0. .
10) методы_count и методы_info , где методы_count представляют количество методов, а методы_информация представляют таблицу методов, где структура таблицы методов показана на рисунке ниже:
Как видно из рисунка выше, структуры Method_info и field_info очень похожи. Все биты флага и значения access_flag в таблице методов показаны на рисунке ниже:
Среди них name_index и descriptor_index представляют имя и дескриптор метода и являются индексами, указывающими на пул констант соответственно. Здесь нам нужно объяснить дескриптор метода. Структура дескриптора метода: (список параметров) возвращаемое значение. Например, дескриптор public int instanceMethod(int param) имеет вид: (I) I, что означает, что он имеет int. параметр типа. И возвращаемое значение также является методом типа int. Далее идет количество атрибутов и таблица атрибутов. Хотя таблица методов и таблица полей имеют количество атрибутов и таблицу атрибутов, они содержат следующие атрибуты. другой. Далее давайте посмотрим на двоичное представление таблицы методов с помощью TestClass. Сначала давайте посмотрим на количество таблиц методов. Скриншот выглядит следующим образом:
Как видно из рисунка выше, количество таблиц методов равно 0×0002, что означает, что существует два метода. Далее давайте проанализируем первый метод. Давайте сначала посмотрим на access_flag, name_index, descriptor_index первого метода TestClass. Скриншот выглядит следующим образом:
Из приведенного выше рисунка мы знаем, что access_flags равен 0×0001. Из приведенного выше описания флага access_flags мы видим, что значение access_flags метода — ACC_PUBLIC, а name_index — 0x000B. Проверьте пул констант. 11-я константа, зная, что имя метода — <init>, 0x000C означает, что descriptor_index означает 12-ю константу в пуле констант, а ее значение — ()V, что означает, что метод <init> не имеет параметров и возвращаемого значения. По сути, это компилятор, автоматически генерирующий методы-конструкторы экземпляров. Следующий 0×0001 указывает, что таблица методов метода <init> имеет 1 атрибут. Скриншот атрибута выглядит следующим образом:
Как видно из рисунка выше, константой в пуле констант, соответствующей 0x000D, является Code, который представляет атрибут Code метода. Так что здесь каждый должен понимать, что коды метода хранятся в атрибуте Code. table в таблице методов файла класса. Далее анализируем атрибут «Код». Структура атрибута «Код» показана на рисунке ниже:
Среди них атрибут_имя_индекс указывает на константу, значением которой является Код в пуле констант, а длина атрибута_длина указывает длину таблицы атрибутов Код (следует отметить, что длина не включает 6-байтовую длину атрибута_имя_индекс и атрибут_длина). ).
max_stack представляет максимальную глубину стека. Виртуальная машина выделяет глубину операндов в кадре стека на основе этого значения во время выполнения, а max_locals представляет пространство для хранения таблицы локальных переменных.
Единицей max_locals является слот, который является наименьшей единицей, в которой виртуальная машина выделяет память для локальных переменных. Во время выполнения типы данных, не превышающие 32-битные типы, такие как byte, char, int и т. д., занимают 1. слот, а двойной и длинный Для 64-битного типа данных необходимо выделить 2 слота. Кроме того, значение max_locals не является суммой памяти, необходимой для всех локальных переменных, поскольку слоты могут использоваться повторно. Когда локальная переменная выходит за пределы своей области видимости, локальная переменная The. занятый слот будет использован повторно.
code_length представляет количество инструкций байт-кода, а code представляет инструкции байт-кода. Из рисунка выше мы можем знать, что тип кода — u1. Значение типа u1 — 0×00-0xFF, а соответствующее десятичное число — 0-. 255. В настоящее время спецификация виртуальной машины определяет более 200 инструкций.
Exception_table_length иException_table соответственно представляют информацию об исключении, соответствующую методу.
Атрибуты_count и атрибут_информация представляют количество атрибутов и таблицу атрибутов в атрибуте «Код» соответственно. Отсюда видно, что таблица атрибутов очень гибка в файловой структуре класса. Она может существовать в файле класса, таблице методов и поле. таблица и атрибут кода.
Далее мы продолжаем анализировать приведенный выше пример. На скриншоте атрибута Code метода init выше мы видим, что длина таблицы атрибутов равна 0×00000026, значение max_stack – 0×0002, а значение max_locals — 0×0001, длина code_length — 0x0000000A, затем 00000149h- 00000152h — это байт-код. Далее длинаException_table_length равна 0×0000, а значение атрибута_count — 0×0001. Значение 00000157h-00000158h — это 0x000E, которое представляет имя атрибута в пуле констант. чтобы получить 14-й. Значение константы — LineNumberTable, LineNu. mberTable используется для описания соответствия между номером строки исходного кода Java и номером строки байт-кода. Это не обязательный атрибут во время выполнения. Если вы отмените генерацию этой информации с помощью параметра компилятора -g:none, это окажет наибольшее влияние. При возникновении исключения номер строки ошибки не может быть отображен в стеке, а точки останова не могут быть установлены в соответствии с исходным кодом во время отладки. Далее давайте посмотрим на структуру LineNumberTable, как показано ниже:
Среди них имя атрибута_index упоминалось выше и представляет собой индекс пула констант, атрибут_длина представляет длину атрибута, а таблицы start_pc и line_number представляют номер строки байт-кода и номер строки исходного кода. Поток байтов свойства LineNumberTable в этом примере показан ниже:
Проанализировав первый метод TestClass выше, мы можем аналогичным образом проанализировать второй метод TestClass. Скриншот выглядит следующим образом:
Среди них access_flags имеет значение 0×0001, name_index — 0x000F, а descriptor_index — 0×0010. Глядя на пул констант, вы можете узнать, что этот метод является общедоступным методом int instanceMethod (int param). С помощью метода, аналогичного приведенному выше, мы можем узнать, что атрибут Code экземпляраMethod такой, как показано на рисунке ниже:
Наконец, давайте проанализируем атрибуты файла классов. 00000191h-00000199h — это таблица атрибутов в файле классов, где 0×0011 представляет имя атрибута. Глядя на пул констант, мы можем узнать, что имя атрибута — SourceFile. Давайте посмотрим на структуру SourceFile следующим образом. Как показано на рисунке:
Среди них атрибут_длина — это длина атрибута, а исходный_индекс указывает на константу в пуле констант, значением которой является имя файла исходного кода. В этом примере снимок экрана атрибута SourceFile выглядит следующим образом:
Среди них длина атрибута равна 0×00000002, что означает, что длина равна 2 байтам, а значение sourcefile_index равно 0×0012. Глядя на 18-ю константу в пуле констант, вы можете узнать, что имя файла исходного кода — TestClass. .java
Наконец, я надеюсь, что друзья, интересующиеся технологиями, смогут больше общаться. Личный Weibo: (http://weibo.com/xmuzyq)