java从编码到执行
01 字节码文件规范 - 图1

java字节码文件被加载到虚拟机上运行,从而实现java跨平台。JVM模式运行方式是解释执行与实时编译执行混合方式,起始采用解释方式,对于多次调用的方法,进行编译执行。
JVM参数:-Xmixed 混合模式、-Xint 纯解释模式、-Xcomp 纯编译模式

  1. /**
  2. *
  3. * 验证JVM代码执行机制
  4. * -Xmixed 默认为混合模式。开始解释执行,启动快,对热点代码进行检测和编译
  5. * -Xint 使用解释模式,启动快,执行稍慢
  6. * -Xcomp 使用纯编译模式,启动慢,运行快
  7. *
  8. * @author :Administrator
  9. * @date :2022/4/25 0025
  10. */
  11. public class Code06_WayToRun {
  12. public static void main(String[] args) {
  13. for (int i = 0; i < 10_0000; i++) {
  14. m();
  15. }
  16. long start = System.currentTimeMillis();
  17. for (int i = 0; i < 10_0000; i++) {
  18. m();
  19. }
  20. long end = System.currentTimeMillis();
  21. // 结果:混合模式 2900左右; 存解释模式:-Xint 很久很久,没跑出接过来; 存编译模式: -Xcomp 3900左右
  22. System.out.println(end - start);
  23. }
  24. private static void m() {
  25. for (long i = 0; i < 10_0000; i++) {
  26. long j = i % 3;
  27. }
  28. }
  29. }

字节码文件

一个什么都没有的类

  1. package com.ixiaoyu2.jvm.bytes;
  2. /**
  3. * @author :Administrator
  4. * @date :2022/4/20 0020
  5. */
  6. public class Code01_BaseDemo {
  7. }
  1. cafe babe 0000 0034 0010 0a00 0300 0d07
  2. 000e 0700 0f01 0006 3c69 6e69 743e 0100
  3. 0328 2956 0100 0443 6f64 6501 000f 4c69
  4. 6e65 4e75 6d62 6572 5461 626c 6501 0012
  5. 4c6f 6361 6c56 6172 6961 626c 6554 6162
  6. 6c65 0100 0474 6869 7301 0028 4c63 6f6d
  7. 2f69 7869 616f 7975 322f 6a76 6d2f 6279
  8. 7465 732f 436f 6465 3031 5f42 6173 6544
  9. 656d 6f3b 0100 0a53 6f75 7263 6546 696c
  10. 6501 0014 436f 6465 3031 5f42 6173 6544
  11. 656d 6f2e 6a61 7661 0c00 0400 0501 0026
  12. 636f 6d2f 6978 6961 6f79 7532 2f6a 766d
  13. 2f62 7974 6573 2f43 6f64 6530 315f 4261
  14. 7365 4465 6d6f 0100 106a 6176 612f 6c61
  15. 6e67 2f4f 626a 6563 7400 2100 0200 0300
  16. 0000 0000 0100 0100 0400 0500 0100 0600
  17. 0000 2f00 0100 0100 0000 052a b700 01b1
  18. 0000 0002 0007 0000 0006 0001 0000 0007
  19. 0008 0000 000c 0001 0000 0005 0009 000a
  20. 0000 0001 000b 0000 0002 000c

字节码文件结构

u1:unsigned one-byte,无符号1字节
u2:unsigned two-byte,无符号2字节
u4 :unsigned three-byte,无符号4字节
相同类型的多条数据集合用表(table)形式存储。表是一个变长的结构,有代表长度的表头n和紧随着n个数据项组成。class文件采用类似C语言的结构体来存储数据。

ClassFile {
  u4 magic;    魔数
  u2 minor_version;    次要版本号
  u2 major_version;    主要版本号
  u2 constant_pool_count;  常量池数量
  cp_info constant_pool[constant_pool_count-1]; 常量池数组,常量池数量-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];  属性数组
}

My Very Cute Animal Turns Savage In Full Moon Areas

  • magic 魔数:魔数供标识类文件格式。java 字节码文件.class 魔数为:0xcafe babe
  • minor_version 次要版本号、major_version 主要版本号:主版本号和副版本号共同决定类文件格式的版本。
  • constant_pool_count 常量池数量:constant_pool_count的值等于表constant_pool的表项数加1。如果constants _pool的索引值大于0且小于constant_pool_count则被认为是有效的。
  • constant_pool[] 常量池表:constant_pool是一个结构表,表示各种字符串常量,类和接口名,字段名,以及ClassFile结构及其子结构中引用的其他常量。每个constant_pool表项的格式由其第一个“标记”字节表示。constant_pool表的索引是从1到constant_pool_count - 1。
  • access_flags 访问标记:access_flags项的值是用于表示此类或接口的访问权限和属性的标志的掩码。
    • 一个接口通过设置的ACC_INTERFACE标志来区分。如果ACC_INTERFACE标志未设置,该类文件定义的是类而不是接口
    • 如果设置了ACC_INTERFACE标志,ACC_ABSTRACT标志也必须设置,ACC_FINAL、ACC_SUPER、ACC_ENUM标志不能设置。
    • 如果没有设置ACC_INTERFACE标志,除了ACC_ANNOTATION之外的其他标志都可以设置。但是,这样的类文件不能同时设置ACC_FINAL和ACC_ABSTRACT标志。
    • ACC_SUPER标志的存在是为了向后兼容由旧的Java编程语言编译器编译的代码。在1.0.2之前的JDK版本中,编译器生成access_flags,其中表示ACC_SUPER的标志没有指定的含义,如果设置了该标志,Oracle的Java虚拟机实现将忽略该标志。
    • ACC_SYNTHETIC标志表示这个类或接口是由编译器生成的,不会出现在源代码中。
    • 注解类型必须设置其ACC_ANNOTATION标志。如果设置了ACC_ANNOTATION标志,ACC_INTERFACE标志也必须设置。
    • ACC_ENUM标志表明这个类或它的超类被声明为枚举类型。 | Flag Name | Value | Interpretation(解释) | | —- | —- | —- | | ACC_PUBLIC | 0x0001 | 标识为public | | ACC_FINAL | 0x0010 | 标识为final,不可被继承 | | ACC_SUPER | 0x0020 | 为了兼容低版本编译的字节码文件。已弃用,忽略 | | ACC_INTERFACE | 0x0200 | 标识为接口 | | ACC_ABSTRACT | 0x0400 | 标识为抽象类 | | ACC_SYNTHETIC | 0x1000 | 标识为编译器自动生成,不是源码编译生成。参考
      https://www.jianshu.com/p/d571300810b3 | | ACC_ANNOTATION | 0x2000 | 标识为注解类型 | | ACC_ENUM | 0x4000 | 标识为枚举类型 |

各位表示如下:
image.png

  • this_class 本类:this_class项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Class_info结构体表示由这个类文件定义的类或接口。
  • super_class 超类:对于类,super_class项的值必须为零,或者必须是constant_pool表的有效索引。如果super_class项的值不为零,则该索引处的constant_pool项必须是一个CONSTANT_Class_info结构体,表示该类文件定义的类的直接超类。如果super_class项的值为0,那么这个类文件必须表示类Object,这是唯一没有直接超类的类或接口。
  • interfaces_count 接口数量:interfaces_count项的值给出该类或接口类型的直接超接口的数量。
  • interfaces[] 接口表:接口数组中的每个值都必须是constant_pool表的有效索引。在interface[i]中,0≤i < interfaces_count,每个值的constant_pool必须是一个CONSTANT_Class_info结构,表示一个接口,该接口是该类或接口类型的直接超接口,按类型源文件中给出的从左到右的顺序。
  • fields_count 字段数量:fields_count项的值给出了fields表中field_info结构的数量。field_info结构体表示由该类或接口类型声明的所有字段,包括类变量和实例变量。
  • fields[] 字段表:fields表中的每个值都必须是一个field_info结构,给出这个类或接口中的一个字段的完整描述。字段表只包含由该类或接口声明的字段。它不包括表示继承自超类或超接口的字段的项。
  • methods_count 方法数:methods_count项的值给出了方法表中method_info结构的数量。
  • methods[] 方法表:方法表中的每个值都必须是一个method_info结构,给出类或接口中一个方法的完整描述。method_info结构体代表该类或接口类型声明的所有方法,包括实例方法、类方法、实例初始化方法,以及任何类或接口初始化方法。方法表不包括表示从超类或超接口继承的方法的项。
  • attributes_count 属性数量:attributes_count项的值给出了该类属性表中属性的数量。
  • attributes[] 属性表:属性表的每个值都必须是一个attribute_info结构

    类名和接口名称二进制名称

    类文件结构中出现的类和接口名称总是以完全限定的形式表示,称为二进制名称。这些名称总是表示为CONSTANT_Utf8_info结构。

    非限定名称 Unqualified Names

    方法、字段、局部变量和形式参数的名称存储为非限定名称。非限定名称必须包含至少一个Unicode码位,且不能包含任何ASCII字符 . ; [ / (即句点、分号、左方括号或正斜杠)。
    方法名被进一步约束,除了特殊的方法名(§2.9),它们不能包含ASCII字符<或>(即左尖括号或右尖括号)。

    描述符

    Grammar Notation 语法符号

  • 语法是一组结果,描述字符序列如何形成各种语法上正确的描述符。

  • 非终止符以斜体显示。非终止符的定义由定义的非终止符的名称后跟冒号引入。一个或多个非终止符的可选定义随后行。
  • 结果右边的语法{x}表示x出现0次或多次。
  • 结果右边的语法{one of}表示下面一行或几行上的每个终端符号都是一个可选定义。

    字段描述符 Field Descriptors

    字段描述符表示类、实例或局部变量的类型。 ```latex FieldDescriptor: FieldType

FieldType: BaseType ObjectType ArrayType

BaseType: (one of) B C D F I J S Z

ObjectType: L ClassName ;

ArrayType: [ ComponentType

ComponentType: FieldType

<a name="cto9A"></a>
##### 字段描述符解释![image.png](https://cdn.nlark.com/yuque/0/2022/png/1636836/1650726642690-53781409-cd89-4931-b0fb-5e998b2530c4.png#clientId=u48e77367-d190-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=594&id=u6fcbcabc&margin=%5Bobject%20Object%5D&name=image.png&originHeight=594&originWidth=716&originalType=binary&ratio=1&rotation=0&showTitle=false&size=62047&status=done&style=none&taskId=u415534fb-d1fc-47c9-a63e-185a5ad0864&title=&width=716)
<a name="fa109"></a>
### 方法描述符 Method Descriptors
方法描述符包含零个或多个参数描述符,表示方法接受的参数类型,以及一个返回描述符,表示方法返回的值类型(如果有的话)。
```latex
MethodDescriptor:
  ( {ParameterDescriptor} ) ReturnDescriptor

ParameterDescriptor:
  FieldType

ReturnDescriptor:
  FieldType
  VoidDescriptor

VoidDescriptor:
  V

例:image.png

常量池表 Constant Pool[]

Java Virtual Machine指令不依赖于类、接口、类实例或数组的运行时布局。相反,指令引用constant_pool表中的符号信息。

常量池格式

cp_info {
  u1 tag;
  u1 info[];
}

constant_pool表中的每一项都必须以一个1字节的标记开始,表示cp_info条目的类型。信息数组的内容随tag的值而变化。
每个标记字节后面必须有两个或多个字节,提供有关特定常量的信息。附加信息的格式随标签值的不同而不同。
image.png

CONSTANT_Class_info 结构

CONSTANT_Class_info 结构用于表示类或接口

CONSTANT_Class_info {
  u1 tag;
  u2 name_index;
}
  • tag tag的值是CONSTANT_Class(7)。
  • name_index name_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Utf8_info结构体,表示以内部形式编码的有效二进制类或接口名,存储类或接口的全限定名。

    CONSTANT_Fieldref_info 结构

    CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }

  • tag:tag的值是CONSTANT_Fieldref (9)

  • class_index:class_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Class_info结构体,表示有字段或方法作为成员的类或接口类型。CONSTANT_Fieldref_info结构体的class_index项可以是类类型也可以是接口类型。
  • name_and_type_index:name_and_type_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_NameAndType_info结构。这个constant_pool条目表示字段或方法的名称和描述符。在CONSTANT_Fieldref_info中,指定的描述符必须是字段描述符。否则,指定的描述符必须是一个方法描述符。

    CONSTANT_Methodref_info 结构

    ```katex CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }

- **tag**:tag的值是CONSTANT_Methodref (10)
- **class_index**:CONSTANT_Methodref_info结构体的class_index项必须是类类型,而不是接口类型。
- **name_and_type_index**:如果CONSTANT_Methodref_info结构的方法名以'<' ('\u003c')开头,那么该名称必须是特殊名称<init>,代表一个实例初始化方法。该方法的返回类型必须为void。
<a name="YDui2"></a>
### CONSTANT_InterfaceMethodref_info 结构
```katex
CONSTANT_InterfaceMethodref_info {
  u1 tag;
  u2 class_index;
  u2 name_and_type_index;
}
  • tag:tag的值是CONSTANT_InterfaceMethodref (11)
  • class_info:CONSTANT_InterfaceMethodref_info结构体的class_index项必须是接口类型

    CONSTANT_String_info 结构体

    结构体CONSTANT_String_info用于表示String类型的常量对象

    CONSTANT_String_info { u1 tag; u2 string_index; }

  • tag:CONSTANT_String_info结构的标签项具有该值CONSTANT_String(8)

  • string_index:string_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Utf8_info结构,表示要初始化String对象的Unicode码位序列。

    CONSTANT_String_info与CONSTANT_Utf8_info的区别

    CONSTAN_Utf8_info存储字符串真正内容,而CONSTANT_String_info只存储指向储存内容的CONSTANT_Utf8_info常量类型的索引。

CONSTANT_Integer_info、CONSTANT_Float_info

CONSTANT_Integer_info和CONSTANT_Float_info结构体表示4字节的数值(int和float)常量

CONSTANT_Integer_info { u1 tag; u4 bytes; } CONSTANT_Float_info { u1 tag; u4 bytes; }

  • tag:CONSTANT_Integer_info结构的tag项有值CONSTANT_Integer(3)。 CONSTANT_Float_info结构的tag项有值CONSTANT_Float(4)。
  • bytes:CONSTANT_Integer_info结构的bytes项表示int常量的值。值的字节以大端顺序(高字节优先)存储。
    CONSTANT_Float_info结构体的bytes项表示IEEE 754浮点单格式中浮点常量的值。单一格式表示的字节以大端顺序(高字节优先)存储。

image.png

CONSTANT_Long_info 、CONSTANT_Double_info

CONSTANT_Long_info和CONSTANT_Double_info表示8字节的数字(长和双)常数

CONSTANT_Long_info { u1 tag; u4 high_bytes; u4 low_bytes; } CONSTANT_Double_info { u1 tag; u4 high_bytes; u4 low_bytes; }

所有8字节常量在类文件的constant_pool表中占据两个条目。如果结构体CONSTANT_Long_info或CONSTANT_Double_info是constant_pool表第n个索引的项,那么该结构体中的下一个可用的项位于第n+2个索引。constant_pool索引n+1必须是有效的,但被认为是不可用的。

  • tag

CONSTANT_Long_info结构的tag项有值CONSTANT_Long(5)。
CONSTANT_Double_info结构的tag项具有该值CONSTANT_Double(6)。

  • high_bytes, low_bytess

CONSTANTLong_info结构的无符号high_bytes和low_bytes项共同表示长常量的值
((long) highbytes << 32) + low_bytes
high_bytes和low_bytes的字节都存储在大端序中(高字节优先)顺序。

CONSTANTDouble_info结构的high_bytes和low_bytes表示IEEE 754浮点双精度浮点格式中的双精度值(§2.3.2)。每个项的字节都以大端顺序(高字节优先)存储。CONSTANT_Double_info结构体表示的值如下所示。high_bytes和low_bytes项被转换为long常量位,等于
((long) highbytes << 32) + low_bytes
image.png

CONSTANT_NameAndType_info 结构

CONSTANT_NameAndType_info结构体用于表示一个字段或方法,而不表明它属于哪个类或接口类型

CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }

  • tag:CONSTANT_NameAndType_info结构体的标签项具有该值CONSTANT_NameAndType(12)。
  • name_index:name_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Utf8_info结构体,表示特殊的方法名)或一个有效的非限定名,表示字段或方法
  • descriptor_index:descriptor_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Utf8_info结构体,表示一个有效的字段描述符或方法描述符。

    CONSTANT_Utf8_info 结构

    结构体CONSTANT_Utf8_info用于表示常量字符串值

    CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

  • tag:CONSTANT_Utf8_info结构的tag项有值
    CONSTANT_Utf8(1)

  • length:length项的值给出了字节数组中的字节数(而不是结果字符串的长度)。
  • bytes[length]:采用MUTF-8编码(Modified UTF8)编码的长度为length的字节数组

bytes数组包含字符串的字节。
没有字节的值可以是(byte)0。
没有字节可以位于(字节)0xf0到(字节)0xff的范围内。

标准的UTF8 编码 Standard UFT-8

标准UTF-8编码是一种变长编码格式,使用1~4字节表示一个字符

  • 对于ASCII编码编码字符(0x0001~0x007F,1~127),UTF-8用一个字节表示,即:

    0000 0001 ~ 0000 007F -> 0xxxx xxxx
    image.png

  • 对于0x0080 ~ 0x07FF范围的字符,UTF-8使用2个字节表示,即:

    0000 0080 ~ 0000 07FF -> 110x xxxx 10xx xxxx
    程序处理这种编码时,先把第一个字节的110和第二个字节的10去掉,剩下的位组成新的2字节的数据,如下图所示:image.png

  • 对于0x0800 ~ 0xFFFF范围的字符,UTF-8使用3个字节表示,即:

0000 0800 ~ 0000 FFFF -> 1110 xxxx 10xx xxxx 10xx xxxx
程序处理这种编码时,先把第一个字节的1110、第二个字节的10、第三个字节的10去掉,剩下的位组成新的3字节的数据,如下图所示:
image.png

  • 对于0x0001 0000 ~ 0x0010 FFFF范围的数据,UTF-8采用4字节表示,即:

    0001 000 ~0010 FFFF -> 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
    程序处理这种编码,先把第一个字节的11110、第二个字节的10、第三个字节的10、第四个字节的10去掉,剩余的字节组成新的4字节的数据,如下图所示
    image.png

    MUTF-8 Modified UTF-8

  • 对于空字符(”\0”),MUTF-8采用2个字节表示,即1100 0000 1000 0000 —> 0xC080;而UTF-8采用1个字节表示,即0x00。在其他语言(如C)中会把空字符当成字符串结束,这样处理的目的是保证字符串中不会出现空字符,因此在C语言处理是不会被意外截断。

  • MUTF-8编码只采用了标准UTF-8编码1、2、3字节编码方式,没有采用4字节编码方式。那么对于编码在oxFFFF之上的字符(java里称为补充字符),java使用 “代理对”(surrogate pair)的方式,使用2个代理字符来进行表示。每个代理字符采用3字节表示。那么补充字符使用6个字节。

image.png

CONSTANT_MethodHandle_info 结构

CONSTANT_MethodHandle_info结构体用于表示一个方法句柄

CONSTANT_MethodHandle_info { u1 tag; u1 reference_kind; u2 reference_index; }

  • tag:CONSTANT_MethodHandle_info结构的标签项具有该值
    CONSTANT_MethodHandle(15)
  • refrence_kind:reference_kind的取值范围是1 ~ 9。该值表示该方法句柄的类型,它表征了其字节码行为
  • refrence_index:reference_index项的值必须是constant_pool表的有效索引

    • 如果reference_kind项的值为1 (REF_getField),则为2
      (REF_getStatic), 3 (REF_putField),或4 (REF_putStatic),那么该索引的constant_pool表项必须是一个CONSTANT_Fieldref_info
      (§4.4.2)结构,表示要为其创建方法句柄的字段。
    • 如果reference_kind项的值为5 (ref_invokvirtual)或8
      (REF_newInvokeSpecial),那么在该索引处的constant_pool条目必须是一个CONSTANT_Methodref_info结构体(§4.4.2),该结构体代表要为其创建方法句柄的类的方法或构造函数(§2.9)
    • 如果reference_kind项的值是6 (REF_invokeStatic)或7 (REF_invokeSpecial),然后如果类文件版本号小于52.0,constant_pool条目在指数必须CONSTANT_Methodref_info结构代表一个类的方法的方法创建句柄;如果类文件的版本号是52.0或更高,那么该索引的constant_pool条目必须是一个CONSTANT_Methodref_info结构体或一个
      CONSTANT_InterfaceMethodref_info结构体(§4.4.2),表示要为其创建方法句柄的类或接口的方法
    • 如果reference_kind项的值是9 (REF_invokeInterface),那么该索引的constant_pool项必须是CONSTANT_InterfaceMethodref_info结构体,表示要为其创建方法句柄的接口方法
    • 如果reference_kind项的值为5 (ref_invokvirtual),则为6
      (REF_invokeStatic), 7 (ref_invokspecial),或9 (REF_invokeInterface),由CONSTANT_Methodref_info结构体或CONSTANT_InterfaceMethodref_info结构体表示的方法名不能是或< clinit >。
    • 如果该值为8 (ref_newinvokspecial),则CONSTANT_Methodref_info结构体表示的方法名必须为

      CONSTANT_MethodType_info 结构体

      CONSTANT_MethodType_info结构体用于表示方法类型

      CONSTANT_MethodType_info { u1 tag; u2 descriptor_index; }

  • tag:CONSTANT_MethodType_info结构的标签项具有该值
    CONSTANT_MethodType(16)

  • descriptor_index:descriptor_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_Utf8_info结构体(§4.4.7)表示一个方法描述符

    CONSTANT_InvokeDynamic_info 结构体

    CONSTANT_InvokeDynamic_info结构体被invokedynamic指令(§invokedynamic)用来指定引导方法、动态调用名称、参数和调用的返回类型,以及可选的一系列附加常量,这些常量被称为引导方法的静态参数

    CONSTANT_InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }

  • tag:CONSTANT_InvokeDynamic_info结构的标记项具有该值
    CONSTANT_InvokeDynamic(18)

  • bootstrap_method_attr_index:bootstrap_method_attr_index项的值必须是该类文件bootstrap方法表(§4.7.23)bootstrap_methods数组的有效索引
  • name_and_type_index:name_and_type_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是CONSTANT_NameAndType_info结构体(§4.4.6)代表一个方法名和方法描述符

    字段表 Feilds

    每个字段由一个field_info结构描述。一个类文件中没有两个字段可以有相同的名称和描述符。

    field_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }

  • access_flags:access_flags项的值是用于表示该字段的访问权限和属性的标志的掩码。每个标志的解释如下: | Flag Name | value | 描述 | | —- | —- | —- | | ACC_PUBLIC | 0x0001 | 声明为public | | ACC_PRIVATE | 0x0002 | 声明为private | | ACC_PROTECTED | 0x0004 | 声明为protected | | ACC_STATIC | 0x0008 | 声明为static | | ACC_FINAL | 0x0010 | 声明为final | | ACC_VOLATILE | 0x0040 | 声明为volatile(解决内存可见性问题) | | ACC_TRANSIENT | 0x0080 | 声明为transient,被transient修饰的字段默认不会被序列化 | | ACC_SYNTHETIC | 0x1000 | 声明这个字段由编译器自动生成,而不是源码编译生成 | | ACC_ENUM | 0x4000 | 声明是一个enum类型的变量 |

  • name_index:name_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是aCONSTANT_Utf8_info结构,它表示一个有效的非限定名,表示一个字段。

  • descriptor_index:escriptor_index项的值必须是constant_pool表的有效索引。该索引的constant_pool条目必须是一个CONSTANT_Utf8_info结构体,它代表一个有效的字段描述符。
  • attributes_count:attributes_count项的值表示该字段的其他属性的数量
  • attribute_info attributes[attributes_count]:属性表的每个值都必须是一个attribute_info结构。一个字段可以有任意数量的与之关联的可选属性。

    方法表 Methods

    每个方法,包括每个实例初始化方法和类或接口初始化方法,都由method_info结构体描述。一个类文件中的两个方法不能有相同的名称和描述符、

    method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }

  • access_flags: | Flag Name | value | 描述 | | —- | —- | —- | | ACC_PUBLIC | 0x0001 | 声明为public | | ACC_PRIVATE | 0x0002 | 声明为private | | ACC_PROTECTED | 0x0004 | 声明为protected | | ACC_STATIC | 0x0008 | 声明为static | | ACC_FINAL | 0x0010 | 声明为final | | ACC_SYNCHRONIZED | 0x0020 | 声明为synchronized | | ACC_BRIDGE | 0x0040 | bridge方法,有编译器生成 | | ACC_VARAGES | 0x0080 | 方法包含可变参数,如StrIng… agrs | | ACC_NATIVE | 0x0100 | 声明为native | | ACC_ABSTRACT | 0x0400 | 声明为abstract | | ACC_STRICT | 0x0800 | 声明为strictfp,表示使用IEEEE-754规范的精确浮点数,极少使用 | | ACC_SYNTHETIC | 0x1000 | 声明这个字段由编译器自动生成,而不是源码编译生成 |

  • name_index:name_index项的值必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是a
    CONSTANT_Utf8_info结构体,表示特殊的方法名,或者表示一个有效的非限定名。

  • descriptor_index:descriptor_index项的值必须是constant_pool表的有效索引。该索引的constant_pool条目必须是一个CONSTANT_Utf8_info结构体,表示有效的方法描述符
  • attributes_count:attributes_count项的值表示此方法的其他属性的数量。
  • attribute_info attributes[attributes_count]:属性表的每个值都必须是一个attribute_info结构

    属性表 Attributes

    属性在ClassFile、field_info、method_info和类文件格式的代码属性结构。所有属性的一般格式如下

    attribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; }

    对于所有属性,attribute_name_index必须是类常量池中的有效的无符号16位索引。attribute_name_index的constant_pool条目必须是一个CONSTANT_Utf8_info结构体,表示属性的名称。attribute_length项的值以字节表示后续信息的长度。长度不包括包含attribute_name_index和attribute_length项的初始6个字节。

JVM规范预定义了23种属性(分为3类):

  1. 有5个属性对于Java正确解释类文件至关重要虚拟机

ConstantValue、Code、StackMapTable、Exceptions、BootstrapMethods

  1. Java SE平台的类库对类文件的正确解释有12个关键属性

InnerClasses、EnclosingMethod、Synthetic、Signature、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations、RuntimeVisibleTypeAnnotations、RuntimeInvisibleTypeAnnotations、AnnotationDefault、MethodParameters

  1. 六个属性对于Java虚拟机或Java SE平台的类库对类文件的正确解释不是至关重要的,但对于工具来说是有用的

SourceFile、SourceDebugExtension、LineNumberTable、LocalVariableTable、LocalVariableTypeTable、Deprecated
注:各种具体属性参考The Java® Virtual Machine Specification Java SE 8 Edition 4.7 Attributes