Глубокое понимание формата файла класса JVM

Java JVM

Мы знаем, что самый известный слоган Java:«Напиши один раз, беги везде», и его независимость от платформы зависит от JVM, все java-файлы компилируются в файлы байт-кода (класса), а виртуальной машине нужно знать только файлы байт-кода. Если вы хотите понять виртуальную машину и механизм загрузки классов, эта часть необходима.

JVM
JVM

Файл класса представляет собой набор бинарных потоков на основе 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.

Есть и другие свойства, если хотите узнать, можете посмотреть ссылки.

Использованная литература:

  1. Java Virtual Machine Specification
  2. Виртуальная машина Java: расширенные функции JVM и рекомендации