Мы знаем, что самый известный слоган Java:«Напиши один раз, беги везде», и его независимость от платформы зависит от JVM, все java-файлы компилируются в файлы байт-кода (класса), а виртуальной машине нужно знать только файлы байт-кода. Если вы хотите понять виртуальную машину и механизм загрузки классов, эта часть необходима.
Файл класса представляет собой набор бинарных потоков на основе 8 байт, все данные в файле класса располагаются без интервала, а многобайтовые данные хранятся в порядке от старшего к старшему. Файлы классов хранят структуры данных в форме псевдокода, близкой к структурам в C, и содержат только две структуры данных: числа без знака и таблицы:
- беззнаковое число:u1, u2, u4, u8 представляют беззнаковые числа размером 1, 2, 4 и 8 байт соответственно.
- поверхность: составной тип данных, состоящий из нескольких чисел без знака или других таблиц, сам файл класса также является таблицей.
Структура таблицы классов:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Ссылаясь на приведенную выше структуру данных, файл класса состоит из 10 частей:
1. Магическое число
2. Основной и дополнительный номера версий файла Class.
3. Постоянный пул
4. Токен доступа
5. Текущее название класса
6. Имя родительского класса
7. Унаследованное имя интерфейса
8. Количество всех включенных полей + поля
9. Количество всех включенных методов + методы
10. Количество всех включенных атрибутов + атрибуты
Ниже мы анализируем каждую часть по очереди:
1. Магическое число
Магическое числоИспользуется для определения типа файла, чтобы определить, является ли файл файлом класса, который может быть принят виртуальной машиной. Многие файлы используют магические числа для определения типа файла, а не расширения (поскольку расширение может быть изменено произвольно). Вы можете увидеть мойГлубокое понимание структуры программы (1).
Магический номер файла класса "0xcafebabe", кофе, детка? Ява сама по себе тоже своего рода кофе Ява, что действительно судьба.
Здесь я также пишу небольшую тестовую программу, чтобы увидеть ее поток бинарного кода:
package com.shuqing28;
public class TestClass {
private int m;
public int inc() {
return m+1;
}
}
Мы используем javac для компиляции файла .class, который можно открыть с помощью WinHex под Windows, а hexdump можно использовать для открытия двоичного файла под Linux.Команда выглядит следующим образом:
$ hexdump -C TestClass.class
00000000 ca fe ba be 00 00 00 34 00 16 0a 00 04 00 12 09 |.......4........|
00000010 00 03 00 13 07 00 14 07 00 15 01 00 01 6d 01 00 |.............m..|
00000020 01 49 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 |.I...<init>...()|
00000030 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e |V...Code...LineN|
00000040 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63 |umberTable...Loc|
00000050 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 |alVariableTable.|
00000060 00 04 74 68 69 73 01 00 19 4c 63 6f 6d 2f 73 68 |..this...Lcom/sh|
00000070 75 71 69 6e 67 32 38 2f 54 65 73 74 43 6c 61 73 |uqing28/TestClas|
00000080 73 3b 01 00 03 69 6e 63 01 00 03 28 29 49 01 00 |s;...inc...()I..|
00000090 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 0e 54 65 |.SourceFile...Te|
000000a0 73 74 43 6c 61 73 73 2e 6a 61 76 61 0c 00 07 00 |stClass.java....|
000000b0 08 0c 00 05 00 06 01 00 17 63 6f 6d 2f 73 68 75 |.........com/shu|
000000c0 71 69 6e 67 32 38 2f 54 65 73 74 43 6c 61 73 73 |qing28/TestClass|
000000d0 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a |...java/lang/Obj|
000000e0 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02 00 |ect.!...........|
000000f0 05 00 06 00 00 00 02 00 01 00 07 00 08 00 01 00 |................|
00000100 09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00 |..../........*..|
00000110 01 b1 00 00 00 02 00 0a 00 00 00 06 00 01 00 00 |................|
00000120 00 03 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c |................|
00000130 00 0d 00 00 00 01 00 0e 00 0f 00 01 00 09 00 00 |................|
00000140 00 31 00 02 00 01 00 00 00 07 2a b4 00 02 04 60 |.1........*....`|
00000150 ac 00 00 00 02 00 0a 00 00 00 06 00 01 00 00 00 |................|
00000160 06 00 0b 00 00 00 0c 00 01 00 00 00 07 00 0c 00 |................|
00000170 0d 00 00 00 01 00 10 00 00 00 02 00 11 |.............|
0000017d
Глядя на шестнадцатеричное число первых 4 байтов первой строки,0xcafebabe
, поэтому тип файла действительно.classдокумент.
2. Номер версии
5-й и 6-й байты — это дополнительный номер версии (Minor Version), а 7-й и 8-й байты — это основной номер версии (Major Version). Здесь мы видим, что наш основной номер версии0x0034
, что равно 52, следующая связь между JDK и соответствующим номером версии:
JDK 1.8 = 52
JDK 1.7 = 51
JDK 1.6 =50
JDK 1.5 = 49
JDK 1.4 = 48
JDK 1.3 = 47
JDK 1.2 = 46
JDK 1.1 = 45
Видно, что я использую скомпилированный код Java8.
3. Постоянный пул
Перейдем к первой строке бинарника:
00000000 ca fe ba be 00 00 00 34 00 16 0a 00 04 00 12 09 |.......4........|
в основном номере версии0x0034
После этого0x0016
Это значение представляет емкость постоянного пула. Константный пул можно понимать как хранилище ресурсов файла класса, а структура данных, содержащаяся в константном пуле, выглядит следующим образом:
cp_info {
u1 tag;
u1 info[];
}
Каждый элемент в пуле констант содержит объект cp_info, начинающийся с тега, который представляет тип константы, а информация имеет собственную структуру в соответствии с различными типами. В настоящее время существует 14 типов констант:
Constant Type | Value |
---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
над0x0016
В десятичном виде это 22, тогда в пуле констант 21 константа, поскольку индекс в пуле констант отсчитывается от 1, поэтому диапазон индекса констант составляет 1–21.
00000000 ca fe ba be 00 00 00 34 00 16 0a 00 04 00 12 09 |.......4........|
Далее смотрим на первую константу пула констант, тэг0x0a
, Убедитесь, что приведенная выше таблица констант является CONSTANT_Methodref, что означает, что следующее определение является методом. Зная тип, мы можем проверить структуру CONSTANT_Methodref. Здесь вы можете обратиться к официальной документации OracleThe class File Format,
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
Поскольку файлы .class являются двоичными файлами без интервала, тогда прочитайте:
- tag:
0x0a
, выше сказано, что это относится к константе CONSTANT_Methodref - class_index: указывает на константу типа CONSTANT_Class_info в пуле констант, представляющую имя вышеуказанного метода.
- name_and_type_index : указывает на константу CONSTANT_NameAndType_info в пуле констант, которая является описанием метода.
Поскольку class_index занимает два байта, он читается следующим0x0004
, то есть 4, указывающее на четвертую константу в пуле констант, name_and_type_index равно0x0012
, указывая на 18-ю константу. 4-я и 18-я константы будут проанализированы позже.
Продолжить чтение, до конца первой строки находится 0x09, указывающий на CONSTANT_Fieldref, указывающий, что следующим является определение поля, проверьте официальный документ, формат:
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
Структура такая же, как у CONSTANT_Methodref_info, и в это время читается вторая строка:
00000010 00 03 00 13 07 00 14 07 00 15 01 00 01 6d 01 00 |.............m..|
индекс_класса0x0003
, указывает на третью константу, name_and_type_index0x0013
Указывает на 13-ю константу. В это время продолжайте читать и, наконец, прочитайте третью константу. На данный момент тег0x07
, таблица может быть получена как тип CONSTANT_Class, константа этого типа представляет собой ссылку на символ класса или интерфейса, структура CONSTANT_Class:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
тег равен 7, name_index равен0x0014
, десятичное число равно 20, что указывает на 20-ю константу, поэтому мы прочитали много байтов. Но анализировать таким образом очень утомительно.К счастью, инструмент javap, который идет в комплекте с java, может помочь нам проанализировать содержимое байт-кода.
Выполните следующую инструкцию:
javap -verbose TestClass.class
Мы можем получить:
Last modified Nov 14, 2017; size 381 bytes
MD5 checksum 102d643185c4823ef103931ff3e34462
Compiled from "TestClass.java"
public class com.shuqing28.TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/shuqing28/TestClass.m:I
#3 = Class #20 // com/shuqing28/TestClass
#4 = Class #21 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/shuqing28/TestClass;
#14 = Utf8 inc
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 TestClass.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = NameAndType #5:#6 // m:I
#20 = Utf8 com/shuqing28/TestClass
#21 = Utf8 java/lang/Object
{
public com.shuqing28.TestClass();
descriptor: ()V
flags: ACC_PUBLIC
...//省略
Здесь мы видим поле пула констант, за которым следует список констант из 21. Видно, что первая константа типа Methodref, class_index указывает на четвертую константу, четвертая константа имеет тип CONSTANT_Class, а name_index указывает на первая константа.20 констант, видно что это константа типа CONSTANT_Utf8.CONSTANT_Utf8 ранее не упоминалась.Следующая ее структура:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
Первый тег равен 1, длина указывает длину массива символов, байты[длина] — это строка, представленная в сокращенной кодировке UTF-8, которая анализируется здесь как com/shuqing28/TestClass, что является полным именем класса. .
Продолжайте возвращаться к первой константе Methodref, чье значение name_and_type_index равно 18, продолжайте находить 18-ю константу, имеющую тип CONSTANT_NameAndType_info, которая представляет информацию о методе:
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
name_index указывает на константу 7, то есть#7 = Utf8 <init>
, является типом CONSTANT_Utf8_info, значение, это имя метода, descriptor_index указывает на константу 8, то есть#8 = Utf8 ()V
, является описанием метода, далее будет сказано, что означает это выражение.
Таким образом, мы можем анализировать эти 21 константу одну за другой.
На самом деле в файле Class в начале перечисляется набор констант, а различные описания, которые следуют, представляют собой различные индексы, указывающие на различные константы в предыдущем пуле констант для описания определения всего класса. Точно так же, как и в словаре, мы используем слова в словаре для составления наших предложений, но предложения в файле класса строго отформатированы Следующее содержимое в основном описывает содержимое класса в фиксированном формате без интервалов.
4. Флаги доступа
После окончания пула констант следующие два байта представляют собой флаги доступа (access_flags).Этот флаг используется для идентификации информации о доступе некоторых классов или интерфейсов, включая то, является ли класс классом или интерфейсом, является ли он абстрактно, и окончательно ли.
Значение токена доступа следующее:
Название логотипа | Значение флага | имея в виду |
---|---|---|
ACC_PUBLIC | 0x0001 | Declared public; may be accessed from outside its package. |
ACC_FINAL | 0x0010 | Declared final; no subclasses allowed. |
ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the invokespecial instruction. |
ACC_INTERFACE | 0x0200 | Is an interface, not a class. |
ACC_ABSTRACT | 0x0400 | Declared abstract; must not be instantiated. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |
ACC_ENUM | 0x4000 | Declared as an enum type. |
Всего в access_flags доступно 16 бит флага, в настоящее время определены только 8, все остальные равны 0, TestClass имеет открытый тип и скомпилирован с помощью компилятора после компиляции JDK1.2 (с использованием компилятора после JDK1.2), этот значение истинно), остальные флаги ложны. Таким образом, значение access_flags должно быть:0x0001|0x0020 = 0x0021
. Мы находим последнюю строку постоянного пула только сейчас:
000000e0 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02 00 |ect.!...........|
65 63 74
Соответствующий ect соответственно, за которым следует0x0021
, что согласуется с результатами нашего анализа.
5. Индекс класса, индекс родительского класса и коллекция индексов интерфейса
Цитируя структуру данных ClassFile в начале статьи, эти три элемента определяются как:
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
И индекс класса, и индекс родительского класса являются данными типа u2, а индекс интерфейса сначала дает количество интерфейсов, а затем массив, содержащий интерфейсы. Эти три значения раскрывают отношения наследования класса.
000000e0 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02 00 |ect.!...........|
Тогда предыдущий0x0021
Смотрите, индекс класса0x0003
, указывающий на третью константу пула констант, проверьте приведенное выше, чтобы получить#3 = Class #20 // com/shuqing28/TestClass
, Третья константа указывает на 20-ю константу, а 20-я константа — это переменная CONSTANT_Utf8, значение которой равно com/shuqing28/TestClass и представляет собой строку полного имени класса.
Далее0x0004
— это индекс родительского класса, указывающий на четвертую константу в пуле констант, то есть#4 = Class #21 // java/lang/Object
, указывает на 21-ю переменную, а именно java/lang/Object, мы знаем, что Object является родительским классом для всех классов.
Далее0x0000
, видно, что TestClass не реализует никакого интерфейса.
6. Коллекция полевых таблиц
Таблицы полей используются для описания переменных, объявленных в интерфейсах или классах. Поля включают переменные уровня класса, а также переменные уровня экземпляра, но не локальные переменные внутри методов. Поле Java может включать следующую информацию: область действия поля, является ли оно переменной экземпляра или переменной класса, является ли оно окончательным, видимость параллелизма (изменчивая), может ли оно сериализоваться (переходное) и данные поля. тип. Ниже приведена конкретная структура таблицы полей:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Посмотрите, что access_flags может принимать следующие значения:
Название логотипа | Значение флага | имея в виду |
---|---|---|
ACC_PUBLIC | 0x0001 | Declared public; may be accessed from outside its package. |
ACC_PRIVATE | 0x0002 | Declared private; usable only within the defining class. |
ACC_PROTECTED | 0x0004 | Declared protected; may be accessed within subclasses. |
ACC_STATIC | 0x0008 | Declared static. |
ACC_FINAL | 0x0010 | Declared final; never directly assigned to after object construction (JLS §17.5). |
ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached. |
ACC_TRANSIENT | 0x0080 | Declared transient; not written or read by a persistent object manager. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ENUM | 0x4000 | Declared as an element of an enum. |
Вообще говоря, может существовать только один из трех флагов ACC_PUBLIC, ACC_PRIVATE и ACC_PROTECTED, а другие флаги основаны на природе самого языка Java.
В задней части флага есть два индекса access_flags, name_index, descriptor_index, две индексные точки представляют собой константный пул, имена полей и представляют собой поля простого метода и дескриптора.
Здесь мы разбираемся в концепции, соответствующие трем словам простое имя, дескриптор и полностью квалифицированное имя:
Полное имя: ранее упомянутое com/shuqing28/TestClass — это полное имя, которое заменяет все «.» в коде Java на «/» и обычно заканчивается «;».
Простое имя: имя метода или поля без типа и модификации, в приведенном выше коде есть "inc" и "m"
Что касается дескриптора метода, то он описывает тип данных, список параметров метода и возвращаемое значение. Мы знаем, что при перегрузке функции в C++ функция фактически переименовывается, включая параметры функции, такие как add(int x, int y), которые после компиляции могут быть Add_Int_Int, но в Java мы помещаем основные данные тип Оба представлены символом верхнего регистра, а класс объекта представлен полным именем объекта L+.
Значение идентификационного символа дескриптора:
идентификационный символ | имея в виду |
---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
S | short |
Z | boolean |
V | void |
L | Объект, например Ljava/lang/Object |
Для массивов добавьте[
просто отлично, какjava.lang.String[][]
, выраженный как[[java/lang/String
, int[]
записывается как[I
.
При описании метода дескриптором описывайте его в порядке списка параметров и возвращаемого значения, причем список параметров также нужно заключать в круглые скобки. Например, упомянутая выше "() V" означает, что параметр пуст, а возвращаемое значение равноvoid
Метод, а именноvoid inc()
метод.
Для более сложного,int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)
, чей дескриптор([CII[CIII) I
.
Продолжаем анализировать бинарный код программы, о которой мы упоминали ранее:
000000e0 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02 00 |ect.!...........|
000000f0 05 00 06 00 00 00 02 00 01 00 07 00 08 00 01 00 |................|
В предыдущем разделе мы проанализировали первую строку0x0000
да, следующий0x01
, Это значение на самом деле представляет собой количество таблиц полей, наш код содержит только одно поле. Далее0x0002
, это поле является флагом access_flags, после запроса оно может быть известно как ACC_PRIVATE, а затем следующее0x0005
, из списка таблицы констант можно узнать, что это#5 = Utf8 m
, за которым следует descriptor_index, значение которого равно0x0006
, проверьте постоянный пул на наличие#6 = Utf8 I
, видно, что это предложениеprivate int m;
Вообще говоря, после descriptor_index также идет коллекция атрибутов, используемая для хранения некоторой дополнительной информации, и0x0000
Не представляет полей атрибутов.
Если поле m объявлено какprivate static int m = 123;
Затем может быть еще одно свойство ConstantValue, указывающее на постоянное значение 123.
7. Коллекция таблиц методов
Коллекция таблиц методов очень похожа на коллекцию таблиц полей и имеет следующую структуру:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Отличаются только параметры доступа к флагам и наборам листов свойств. Например, access_flags имеет следующие необязательные значения:
Название логотипа | Значение флага | имея в виду |
---|---|---|
ACC_PUBLIC | 0x0001 | Declared public ; may be accessed from outside its package. |
ACC_PRIVATE | 0x0002 | Declaredprivate ; accessible only within the defining class. |
ACC_PROTECTED | 0x0004 | Declaredprotected ; may be accessed within subclasses. |
ACC_STATIC | 0x0008 | Declaredstatic . |
ACC_FINAL | 0x0010 | Declaredfinal ; must not be overridden |
ACC_SYNCHRONIZED | 0x0020 | Declaredsynchronized ; invocation is wrapped by a monitor use. |
ACC_BRIDGE | 0x0040 | A bridge method, generated by the compiler. |
ACC_VARARGS | 0x0080 | Declared with variable number of arguments. |
ACC_NATIVE | 0x0100 | Declarednative ; implemented in a language other than Java. |
ACC_ABSTRACT | 0x0400 | Declaredabstract ; no implementation is provided. |
ACC_STRICT | 0x0800 | Declaredstrictfp ; floating-point mode is FP-strict. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
Видно, что метод добавляет что-то вродеACC_SYNCHRONIZED
,ACC_NATIVE
,ACC_STRICT
, ACC_ABSTRACT
, соответствующийsynchronized
,native
,strictfp
,abstract
Это может только изменять ключевые слова метода.
Теперь мы можем перейти к анализу бинарного кода нашей программы.
000000f0 05 00 06 00 00 00 02 00 01 00 07 00 08 00 01 00 |................|
00000100 09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00 |..../........*..|
В предыдущем разделе мы только что проанализировали 0x0000 строки 000000f0, а в следующем0x0002
, представляющий два метода, следующие несколько байтов
-
0x0001
: Маркер доступа — ACC_PUBLIC. -
0x0007
: Индекс имени указывает на 7-ю константу: -
0x0008
: Индекс дескриптора указывает на 8-й константу: () v -
0x0001
: В собственности есть -
0x0009
: Атрибут указывает на девятую константу Code
У нас просто есть вопрос, метод определен, где находится тело метода, и ответ — последний Код, проанализированный выше. В следующем разделе обсуждаются различные возможности коллекций таблиц атрибутов.
8. Коллекция листа свойств
Таблица атрибутов (attribute_info) упоминалась ранее много раз.Файлы классов, таблицы полей и таблицы методов могут содержать собственный набор таблиц атрибутов для описания информации, передаваемой в определенных сценариях.
Таблица атрибутов строго не ограничивает порядок. Пока она не дублирует существующее имя атрибута, компилятор, реализованный кем угодно, может добавить свою собственную определенную информацию об атрибутах. Ниже приведены некоторые предопределенные атрибуты:
Имя свойства | используемое местоположение | имея в виду |
---|---|---|
SourceFile | ClassFile | Запишите имя исходного файла |
InnerClasses | ClassFile | внутренний список классов |
EnclosingMethod | ClassFile | Только внутренние классы имеют этот атрибут, который используется для идентификации метода окружения, в котором находится этот класс. |
SourceDebugExtension | ClassFile | Используется для хранения дополнительной отладочной информации, новой в JDK1.6. |
BootstrapMethods | ClassFile | Квалификатор метода загрузки для сохранения ссылок на вызываемые динамические инструкции, новое в JDK1.7 |
ConstantValue | field_info | Постоянное значение, определяемое последним ключевым словом |
Code | method_info | Код Java, скомпилированный в инструкции байт-кода |
Exceptions | method_info | исключение, вызванное методом |
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations | method_info | Укажите, какие параметры видны во время выполнения, а какие невидимы во время выполнения, новое в JDK1.5. |
AnnotationDefault | method_info | Запишите значение по умолчанию для элемента класса аннотаций, новое в JDK1.5. |
MethodParameters | method_info | Запишите информацию о параметрах метода, такую как их имя, уровень доступа, новое в JDK1.8. |
Synthetic | ClassFile, field_info, method_info | Он представляет собой поле или метод автоматически генерируется компилятором |
Deprecated | ClassFile, field_info, method_info | Поля, объявленные устаревшими |
Signature | ClassFile, field_info, method_info | Сигнатура метода, используемая для поддержки универсальных шаблонов.В языке Java, если универсальная сигнатура любого класса, интерфейса, метода инициализации или члена содержит переменную типа или параметризованный тип, свойство Signature будет записывать для него информацию об универсальной сигнатуре. Поскольку дженерики Java реализуются методом стирания, во избежание путаницы в подписях после стирания информации о типе этот атрибут требуется для записи соответствующей информации в дженерики. Добавлено в JDK1.5 |
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations | ClassFile, field_info, method_info | Обеспечить поддержку динамических аннотаций, указывающих, какие аннотации видны во время выполнения, а какие невидимы во время выполнения, новое в JDK1.5. |
LineNumberTable | Code | Соответствие между номерами строк исходного кода Java и инструкциями байт-кода |
LocalVariableTable | Code | описание локальной переменной метода |
LocalVariableTypeTable | Code | Добавлено использование сигнатур функций вместо дескрипторов для описания универсальных параметризованных типов после введения универсального синтаксиса.Новое в JDK1.5 |
StackMapTable | Code | Чтобы средство проверки нового типа (Type Checker) проверяло и обрабатывало, соответствуют ли локальные переменные целевого метода типам, требуемым стеком операций, новое в JDK1.6. |
RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations | ClassFile, field_info, method_info, Code | Записывает видимость аннотаций к типам во время выполнения, включая видимость аннотаций к параметрам типа во время выполнения. |
Вот некоторые из наиболее важных свойств:
Свойство кода
Сначала взгляните на структуру свойства Code:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- attribute_name_index: Занимает два байта, указывает на константу CONSTANT_Utf8_info, указывающую имя атрибута, здесь фиксированный "Код"
- attribute_length: Длина значения атрибута. Поскольку attribute_name_index и attribute_length занимают 6 байт, attribute_length — это длина таблицы атрибутов минус 6.
- max_statck: максимальное значение глубины стека операндов.При выполнении метода стек операндов не может превышать это значение.
-
max_locals: Место для хранения, необходимое для локальных переменных. Единицей max_locals является слот, который является наименьшей единицей, выделенной виртуальной машиной для локальных переменных.Для типов данных, длина которых не превышает 32 бит, таких как byte, char, float, int, short, boolean и returnAddress, они только занимают один слот, в то время как двойные 64-битные данные, такие как long и long, должны занимать два слота. параметры метода (включая скрытые
this
), параметры обработчика исключений и локальные переменные, определенные телом метода, должны храниться в таблице локальных переменных. Но max_locals — это не сумма слотов, занятых всеми локальными переменными, потому что слоты можно использовать повторно.Когда переменная выходит за пределы области видимости, слот будет использоваться другими локальными переменными, и компилятор рассчитает max_locals в соответствии с областью видимости. - code_length: длина байт-кода, скомпилированного компилятором
- code: ряд потоков байтов, используемых для хранения инструкций байт-кода. Каждая инструкция представляет собой один байт типа u1. Когда виртуальная машина считывает байт, она может узнать, что это за инструкция. Если вы знаете, какие операнды требуются инструкции, вы можете просто продолжить чтение. Это похоже на ассемблер. Диапазон значений u1 составляет 0 ~ 255, что может выражать 256 инструкций. Около 200 инструкций определены в спецификации виртуальной машины Java, см.Instructions. Я напишу блог об этой части позже.
- exception_table_length: Длина таблицы исключений
- exception_table: таблица исключений не обязательно должна существовать для кода, поэтому указанная выше длина также может быть равна 0. Таблица исключений имеет 4 атрибута, что означает, что если между start_pc и end_pc возникнет исключение типа catch_type, оно перейдет к точке handler_pc.обработка строк.
Свойство исключений
Атрибут Exceptions находится на том же уровне, что и атрибут Code в таблице методов.Обратите внимание, что, в отличие от таблицы исключений в приведенном выше коде, атрибут Exceptions используется для перечисления исключений, которые может генерировать метод.Структура атрибута Exceptions стол:
Exceptions_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_exceptions;
u2 exception_index_table[number_of_exceptions];
}
- number_of_exceptions: количество типов исключений, которые могут быть выброшены
- exception_index_table: индексировать CONSTANT_Class_info в пуле констант.
Свойство LineNumberTable
LineNumber используется для записи соответствия между исходным кодом Java и номером строки байткода, мы также можем использовать его при компиляции кода-g: none
или-g: line
чтобы отменить генерацию этого свойства, но при отладке кода вы не видите номер строки, и вы не можете сломать точку.
Структура данных LineNumberTable выглядит следующим образом:
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
В основном мы смотрим на line_number_table, start_pc — это номер строки байт-кода, а line_number — это номер строки исходного кода Java.
Свойство LocalVariableTable
Атрибут LocalVariableTable используется для описания связи между переменными в таблице локальных переменных в кадре стека и переменными, определенными в исходном коде Java. Мы также можем использовать его при компиляции кода.-g: none
или-g: vars
, чтобы отменить создание этого атрибута, но в случае отмены среда IDE заменит исходное имя параметра такими параметрами, как arg0 и arg1, что приведет к нечеткой отладке.
Структура данных LocalVariableTable выглядит следующим образом:
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
В основном введите local_variable_table:
- start_pc и длина: представляет смещение байт-кода в начале жизненного цикла этой локальной переменной и длину покрытия области.
- имя_индекс и дескриптор_индекс: указывает на константы, представляющие имена локальных переменных и дескрипторы локальных переменных соответственно.
- index: позиция слота локальной переменной в таблице локальных переменных.Если переменная имеет тип double или long, занятые слоты имеют индекс и индекс+1.
Свойство ConstantValue
ConstantValue — это атрибут фиксированной длины, используемый для уведомления виртуальной машины о назначении значений статическим переменным, если они определены одновременно.int x=3;
а такжеstatic int y=3;
Затем виртуальная машина в разное время присваивает значения x и y.Для x это находится в конструкторе экземпляра.<init>
в переменной статического типа она будет в конструкторе класса<clinit>
Способ или используйте свойства ConstantValue.
В настоящее время правило компилятора Javac заключается в том, что если есть окончательная и статическая модификация одновременно, это используемое свойство ConstantValue, только статический, а тип переменного является примитив или строка, они будут в<clinit>
инициализирован в.
Свойство InnerClasses
Если в классе определен внутренний класс, свойство InnerClasses используется для записи отношения между внутренним классом и хостом.
Структура данных InnerClasses выглядит следующим образом:
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes; //记录有多少个内部类
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
Или просто посмотрите на поле классов, inner_class_info_index указывает на символическую ссылку внутреннего класса, external_class_info_index указывает на символическую ссылку основного класса, inner_name_index указывает на имя внутреннего класса, если это анонимный внутренний класс, это 0, inner_class_access_flags — это флаг доступа внутреннего класса, см. следующую таблицу:
Название логотипа | Значение флага | имея в виду |
---|---|---|
ACC_PUBLIC | 0x0001 | Marked or implicitly public in source. |
ACC_PRIVATE | 0x0002 | Marked private in source. |
ACC_PROTECTED | 0x0004 | Marked protected in source. |
ACC_STATIC | 0x0008 | Marked or implicitly static in source. |
ACC_FINAL | 0x0010 | Marked final in source. |
ACC_INTERFACE | 0x0200 | Was an interface in source. |
ACC_ABSTRACT | 0x0400 | Marked or implicitly abstract in source. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |
ACC_ENUM | 0x4000 | Declared as an enum type. |
Есть и другие свойства, если хотите узнать, можете посмотреть ссылки.
Использованная литература: