# 参考资料
- Chapter 4. The class File Format, oracle 官方技术规范
- Chapter 6. The Java Virtual Machine Instruction Set
- Chapter 7. Opcode Mnemonics by Opcode
# 无关性的基石
字节码(ByteCode)

# Class 类文件的结构
查看类文件结构的命令
javap -verbose <类文件>
| 类型 | 名称 | 数量 |
|---|---|---|
| u4 | magic | 1 |
| u2 | minor_version | 1 |
| u2 | major_version | 1 |
| u2 | constant_pool_count | 1 |
| cp_info | constant_pool | constant_pool_count - 1 |
| u2 | access_flags | 1 |
| u2 | this_class | 1 |
| u2 | super_class | 1 |
| u2 | interfaces_count | 1 |
| u2 | interfaces | interfaces_count |
| u2 | fields_count | 1 |
| field_info | fields | fields_count |
| u2 | methods_count | 1 |
| method_info | methods | methods_count |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
# 魔数与版本号
魔数:固定 0xCAFEBABE (咖啡宝贝)
Class 文件的主版本号:
- Java SE 9 = 53 [hex: 35]
- Java SE 8 = 52 [hex: 34]
- Java SE 7 = 51 [hex: 33]
- Java SE 6.0 = 50 [hex: 32]
- Java SE 5.0 = 49 [hex: 31]
- JDK 1.4 = 48
- JDK 1.3 = 47
- JDK 1.2 = 46
- JDK 1.1 = 45
# 常量池
常量池从 #1 开始表示,常量池数量会比实际数量多 1,实际数量是
# 存储内容
字面量:
- 文本字符串
- 声明为 final 的常量值等
符号引用(Symbolic References):
- 类和接口的全限定名(Fully Qualified Name)
- 字段的名称和描述符(Descriptor)
- 方法的名称和描述符
# 常量池格式
cp_info { | |
u1 tag; | |
u1 info[]; | |
} |
# tag 标记
| 类型 | 标志 | 描述 |
|---|---|---|
| CONSTANT_Utf8 | 1 | UTF8 字符串 |
| CONSTANT_Integer | 3 | 整型 |
| CONSTANT_Float | 4 | 单精度浮点型 |
| CONSTANT_Long | 5 | 长整型 |
| CONSTANT_Double | 6 | 双精度浮点型 |
| CONSTANT_Class | 7 | 类或接口 |
| CONSTANT_String | 8 | 字符串 |
| CONSTANT_Fieldref | 9 | 类的字段 |
| CONSTANT_Methodref | 10 | 类的方法 |
| CONSTANT_InterfaceMethodref | 11 | 类的接口方法 |
| CONSTANT_NameAndType | 12 | 字段或方法 |
| CONSTANT_MethodHandle | 15 | 方法句柄 |
| CONSTANT_MethodType | 16 | 方法类型 |
| CONSTANT_InvokeDynamic | 18 | 动态方法调用点 |
CONSTANT_MethodHandle、CONSTANT_MethodType、CONSTANT_InvokeDynamic 动态调用
# info 信息
# Utf8
CONSTANT_Utf8_info { | |
u1 tag; | |
u2 length; | |
u1 bytes[length]; | |
} |
- length: bytes 的个数。
- bytes: 不是 null-terminated,即不是以 '\0' 结尾的字符串,没有结尾符。
# Intefer, Float
CONSTANT_Integer_info { | |
u1 tag; | |
u4 bytes; | |
} | |
CONSTANT_Float_info { | |
u1 tag; | |
u4 bytes; | |
} |
- bytes: 两者都是 big-endian 存储方式;Float 的按 IEEE 754 floating-point single format 存储。
# Long, Double
CONSTANT_Long_info { | |
u1 tag; | |
u4 high_bytes; | |
u4 low_bytes; | |
} | |
CONSTANT_Double_info { | |
u1 tag; | |
u4 high_bytes; | |
u4 low_bytes; | |
} |
- high_bytes, low_bytes: 两者都是 big-endian 存储方式;Double 的按 IEEE 754 floating-point double format 存储
# Class
CONSTANT_Class_info { | |
u1 tag; | |
u2 name_index; | |
} |
- name_index: 指向的常量池下标对应的内容是 CONSTANT_Utf8 类型的,类名称的字符串。
# String
CONSTANT_String_info { | |
u1 tag; | |
u2 string_index; | |
} |
- string_index: 指向的常量池下标对应的内容是 CONSTANT_Utf8 类型的,字符串。
# Fieldref, Methodref, InterfaceMethodref
CONSTANT_Fieldref_info { | |
u1 tag; | |
u2 class_index; | |
u2 name_and_type_index; | |
} | |
CONSTANT_Methodref_info { | |
u1 tag; | |
u2 class_index; | |
u2 name_and_type_index; | |
} | |
CONSTANT_InterfaceMethodref_info { | |
u1 tag; | |
u2 class_index; | |
u2 name_and_type_index; | |
} |
- class_index: 指向的常量池下标对应的内容是 CONSTANT_Class 类型的,所属的类。
- name_and_type_index: 指向的常量池下标对应的内容是 CONSTANT_NameAndType 类型的
# NameAndType
CONSTANT_NameAndType_info { | |
u1 tag; | |
u2 name_index; | |
u2 descriptor_index; | |
} |
- name_index: 指向的常量池下标对应的内容是 CONSTANT_Utf8 类型的,字段或方法的名称。
- descriptor_index: 指向的常量池下标对应的内容是 CONSTANT_Utf8 类型的,字段或方法描述符。
# MethodHandle
CONSTANT_MethodHandle_info { | |
u1 tag; | |
u1 reference_kind; | |
u2 reference_index; | |
} |
reference_kind: 值必须在 1~9 范围,方法句柄的类型,类型表示方法句柄的字节码行为,参考(§5.4.3.5)。
reference_index: 常量池的引用下标:
- 如果 reference_kind 的值是 1~4,则引用的常量池下标对应的内容是 CONSTANT_Fieldref 类型
- 如果 reference_kind 的值是 5~8,则引用的常量池下标对应的内容是 CONSTANT_Methodref 类型
- 如果 reference_kind 的值是 9,则引用的常量池下标对应的内容是 CONSTANT_InterfaceMethodref 类型
- 如果 reference_kind 的值是 5,6,7 或 9,则方法或接口的名称一定不是
<init>或<clinit> - 如果 reference_kind 的值是 8,则方法的名称一定是
<init>
# MethodType
CONSTANT_MethodType_info { | |
u1 tag; | |
u2 descriptor_index; | |
} |
- descriptor_index: 引用的常量池下标对应的内容是 CONSTANT_Utf8 类型的,方法的描述符。
# InvokeDynamic
CONSTANT_InvokeDynamic_info { | |
u1 tag; | |
u2 bootstrap_method_attr_index; | |
u2 name_and_type_index; | |
} |
- bootstrap_method_attr_index: 当前类文件中引导方法表的 bootstrap_methods [] 数组的有效索引。
- name_and_type_index: 引用的常量池下标的内容是 CONSTANT_NameAndType 类型的,字段或方法的描述符。
# 访问标志
| 标志名称 | 标志值 | 描述 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | public 类型 |
| ACC_FINAL | 0x0010 | final 类型 |
| ACC_SUPER | 0x0020 | 使用 invokespecial 新指令语义 |
| ACC_INTERFACE | 0x0200 | 接口 |
| ACC_ABSTRACT | 0x0400 | 抽象类 |
| ACC_SYNTHETIC | 0x1000 | 表明由编译器产生 |
| ACC_ANNOTATION | 0x2000 | 注解 |
| ACC_ENUM | 0x4000 | 枚举 |
invokespecial 语义在 JDK 1.0.2 发生改变,因此 JDK 1.0.2 之后编译出的类,ACC_SUPER 一定设置。
# 类 / 父类索引,接口索引集合
# 类索引和父类索引
引用常量池下标,对应的内容是 CONSTANT_Class 类型的结构,该结构中的 name_index 指向 CONSTANT_Utf8 类型的结构,从中可以获取类的全限定名字符串。
# 接口索引集合
interfaces_count
接口计数器,表示索引表的容量,如果没实现任何接口,则该项为 0,以及接下来的接口集合也为空,不再占字节。
接口也是引用常量池下标,对应的内容也是是 CONSTANT_Class 的结构。
# 字段表集合
# fields_count
字段计数器,表明字段表集合包含字段信息的数量。
# 字段表结构
field_info { | |
u2 access_flags; | |
u2 name_index; | |
u2 descriptor_index; | |
u2 attributes_count; | |
attribute_info attributes[attributes_count]; | |
} |
# access_flags
| 标志名称 | 标志值 | 描述 |
|---|---|---|
| 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 修饰符 |
| ACC_SYNTHETIC | 0x1000 | 表明是由编译器生成 |
| ACC_ENUM | 0x4000 | enum 类型 |
# name_index
字段的名称引用,CONSTANT_Utf8 类型。
# descriptor_index
字段的全限定描述符引用,CONSTANT_Utf8 类型。
# attributes_count
属性表计数。
# attributes
# 方法表集合
# methods_count
方法表集合的计数,表明方法表集合包含方法信息的数量。
# 方法表结构
method_info { | |
u2 access_flags; | |
u2 name_index; | |
u2 descriptor_index; | |
u2 attributes_count; | |
attribute_info attributes[attributes_count]; | |
} |
# access_flags
| 标志名称 | 标志值 | 描述 |
|---|---|---|
| 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 | 表明由编译器产生的桥接方法 |
| ACC_VARARGS | 0x0080 | 表明方法接收不定参数 |
| ACC_NATIVE | 0x0100 | native 修饰符 |
| ACC_ABSTRACT | 0x0400 | abstract 修饰符 |
| ACC_STRICT | 0x0800 | strictfp 修饰符 |
| ACC_SYNTHETIC | 0x1000 | 表明由编译器生成 |
strictfp 修饰符,floating-point mode is FP-strict,浮点数使用精确模式
# name_index
方法名,CONSTANT_Utf8 类型。
# descriptor_index
方法全限定名描述符,CONSTANT_Utf8 类型。
# attributes_count
属性表集合计数
# attributes
# 属性表集合
属性表集合会在 ClassFile, field_info, method_info, 和 Code_attribute 结构中使用。
# attributes_count
属性表集合计数
# 属性表结构
attribute_info { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u1 info[attribute_length]; | |
} |
# attribute_name_index
属性名,引用常量池的下标,对应的内容是 CONSTANT_Utf8 类型的,固定值如下表。
# attribute_length
表明 info 字节的数量
# 预定义的类文件属性集合
| 属性 | since | class file |
|---|---|---|
| ConstantValue | 1.0.2 | 45.3 |
| Code | 1.0.2 | 45.3 |
| StackMapTable | 6 | 50.0 |
| Exceptions | 1.0.2 | 45.3 |
| InnerClasses | 1.1 | 45.3 |
| EnclosingMethod | 5.0 | 49.0 |
| Synthetic | 1.1 | 45.3 |
| Signature | 5.0 | 49.0 |
| SourceFile | 1.0.2 | 45.3 |
| SourceDebugExtension | 5.0 | 49.0 |
| LineNumberTable | 1.0.2 | 45.3 |
| LocalVariableTable | 1.0.2 | 45.3 |
| LocalVariableTypeTable | 5.0 | 49.0 |
| Deprecated | 1.1 | 45.3 |
| RuntimeVisibleAnnotations | 5.0 | 49.0 |
| RuntimeInvisibleAnnotations | 5.0 | 49.0 |
| RuntimeVisibleParameterAnnotations | 5.0 | 49.0 |
| RuntimeInvisibleParameterAnnotations | 5.0 | 49.0 |
| AnnotationDefault | 5.0 | 49.0 |
| BootstrapMethods | 7 | 51.0 |
# ConstantValue
位置:field_info
field_info 结构中的 access_flags 是 ACC_STATIC 才有该属性;
Sun javac 的实现是字段被 static 和 final 修饰才有该属性,其它的初始化是在 <cinit> 中赋值。
ConstantValue_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 constantvalue_index; | |
} |
constantvalue_index
引用常量池的下标,对应的内容的类型根据字段类型不同而不同,类型对应下面的表格。
| 字段类型 | 常量类型 |
|---|---|
| long | CONSTANT_Long |
| float | CONSTANT_Float |
| double | CONSTANT_Double |
| int, short, char, byte, boolean | CONSTANT_Integer |
| String | CONSTANT_String |
# Code
位置:method_info
如果方法是 native 或 abstract,则 method_info 中不会有 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; | |
Exception_info exception_table[exception_table_length]; | |
u2 attributes_count; | |
attribute_info attributes[attributes_count]; | |
} | |
Exception_info { | |
u2 start_pc; | |
u2 end_pc; | |
u2 handler_pc; | |
u2 catch_type; | |
} |
max_stack
方法执行时的最大操作数堆栈 (Operand Stacks) 深度,虚拟机根据这个值来分配栈帧的操作数深度。
max_locals
局部变量表所需的存储空间,单位是 Slot;boolean, byte, char, short, int, float, reference 和 returnAddress 占 1 个 Slot,long 和 double 占 2 个 Slot。
code_length
字节码(code)长度。
code
字节码
exception_table_length
exception_table 的数量。
exception_table
异常处理使用异常表实现,而不是简单的跳转。
- [start_pc, end_pc): 记录的是 try-catch 之间的代码行范围,在 code 数组中的下标,异常处理器(exception handler)在该范围的代码行中激活,如果该范围的代码出现了 catch_type 类型的异常,则会跳到 handler_pc 处理异常。
- handler_pc: 记录的是开始处理异常的代码行,在 code 数组中的下标。
- catch_type: 捕获的异常,指向常量池的下标,对应的内容类型是 CONSTANT_Class;可以指定 0,则必定跳到 handler_pc 中处理,因此,可以作为 finally 的实现。
attributes_count
attributes 中的数量。
attributes
该节是讨论属性集合的,参考
# StackMapTable
位置:Code 属性
参考
StackMapTable_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 number_of_entries; | |
stack_map_frame entries[number_of_entries]; | |
} |
number_of_entries
entries 的数量。
entries
union stack_map_frame { | |
same_frame; | |
same_locals_1_stack_item_frame; | |
same_locals_1_stack_item_frame_extended; | |
chop_frame; | |
same_frame_extended; | |
append_frame; | |
full_frame; | |
} |
# Exceptions
位置:method_info
表明该方法可能抛出哪些异常;在 Java 中,throws 关键字的实现。
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 的数量。
exception_index_table
抛出的异常,引用常量池的下标,对应的内容类型是 CONSTANT_Class。
# InnerClasses
位置:ClassFile
内部类。
InnerClasses_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 number_of_classes; | |
Class_info classes[number_of_classes]; | |
} | |
Class_info { | |
u2 inner_class_info_index; | |
u2 outer_class_info_index; | |
u2 inner_name_index; | |
u2 inner_class_access_flags; | |
} |
number_of_classes
classes 的数量。
classes
- inner_class_info_index: 内部类;引用常量池的下标,对应的内容类型是 CONSTANT_Class。
- outer_class_info_index: 宿主类,也是引用 CONSTANT_Class 类型的常量池下标,如果该类是顶级(top-level)的类或接口,以及或者是本地类(方法内部的类)、匿名类,则该项为 0。
- inner_name_index: 内部类的名称,如果是匿名内部类,则该值为 0;否则引用 CONSTANT_Utf8 类型的常量池下标。
inner_class_access_flags,访问权限
| 标志名称 | 标志值 | 描述 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | public 修饰符 |
| ACC_PRIVATE | 0x0002 | private 修饰符 |
| ACC_PROTECTED | 0x0004 | protected 修饰符 |
| ACC_STATIC | 0x0008 | static 修饰符 |
| ACC_FINAL | 0x0010 | final 修饰符 |
| ACC_INTERFACE | 0x0200 | interface 类 |
| ACC_ABSTRACT | 0x0400 | abstract 修饰符 |
| ACC_SYNTHETIC | 0x1000 | 表明是由编译器生成 |
| ACC_ANNOTATION | 0x2000 | annotation 类型 |
| ACC_ENUM | 0x4000 | enum 类型 |
# EnclosingMethod
位置:ClassFile
如果是匿名类或本地类 (local class) 才会存在该属性。
EnclosingMethod_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 class_index; | |
u2 method_index; | |
} |
class_index
该类的全限定名引用,引用常量池下标,对应的内容类型是 CONSTANT_Class
method_index
如果该类不被方法或构造器包围,则该项为 0;否则引用 CONSTANT_NameAndType 类型的常量池。
# Synthetic
位置:ClassFile, field_info, 或 method_info
ACC_SYNTHETIC 标记编译器生成的,不显示在源文件上的类、字段、方法,这些的属性集合里都会存在该属性
Synthetic_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
} |
- attribute_length: 固定为 0。
# Signature
位置:ClassFile, field_info, 或 method_info
签名,Java 通过反射机制获取泛型类型;
因为采用擦除法实现伪泛型,编译后就没有信息了,Signature 是弥补该缺点。
Signature_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 signature_index; | |
} |
- signature_index: 引用常量池下标,对应的内容类型是 CONSTANT_Utf8。
# SourceFile
位置:ClassFile,可选
javac -g:none / -g:source 关闭或开启生成源码文件名称。
SourceFile_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 sourcefile_index; | |
} |
sourcefile_index
源码文件名称,引用常量池的下标,对应的内容类型是 CONSTANT_Utf8。
# SourceDebugExtension
位置:ClassFile,可选
保存额外的调试信息,JSP 调试用到。
SourceDebugExtension_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u1 debug_extension[attribute_length]; | |
} |
debug_extension
UTF8 字节,没有结尾符 '\0'。
# LineNumberTable
位置:Code,可选
javac -g:none / -g:lines 关闭和开启,保存代码的行号,抛异常时可以查看到对应行号。
LineNumberTable_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 line_number_table_length; | |
LineNumberTable line_number_table[line_number_table_length]; | |
} | |
LineNumberTable { | |
u2 start_pc; | |
u2 line_number; | |
} |
- start_pc: 字节码行号
- line_number: 源码行号
# LocalVariableTable
位置:Code,可选
javac -g:none / -g:vars 关闭和开启,保存局部变量的名称;如果没保存,则 IDE 使用 arg0、arg1 等类似的占位符。
LocalVariableTable_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 local_variable_table_length; | |
LocalVariableTable local_variable_table[local_variable_table_length]; | |
} | |
LocalVariableTable { | |
u2 start_pc; | |
u2 length; | |
u2 name_index; | |
u2 descriptor_index; | |
u2 index; | |
} |
- [start_pc, start_pc + length): 该局部变量作用域的范围。
- name_index: 变量名,引用 CONSTANT_Utf8 类型的常量池。
- descriptor_index: 变量的全限定名称,引用 CONSTANT_Utf8 类型的常量池。
- index: 在栈帧局部变量表中 Slot 的位置,如果是 long 或 double 类型,则占用 Slot 的 index 和 index + 1。
# LocalVariableTypeTable
位置:Code,可选
泛型中使用,签名。
与 LocalVariableTable 类似,仅 signature_index 不一样。
LocalVariableTypeTable_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 local_variable_type_table_length; | |
LocalVariableTypeTable local_variable_type_table[local_variable_type_table_length]; | |
} | |
LocalVariableTypeTable { | |
u2 start_pc; | |
u2 length; | |
u2 name_index; | |
u2 signature_index; | |
u2 index; | |
} |
- signature_index: 引用 CONSTANT_Utf8 类型的常量池。
# Deprecated
位置:ClassFile, field_info, 或 method_info
@deprecated 表明一个类、字段或方法已经不推荐使用了。
Deprecated_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
} |
# RuntimeVisibleAnnotations
位置:ClassFile, field_info, 或 method_info
记录在运行时,可见的注解;通过 Java 反射 API 可以获取注解信息。
RuntimeVisibleAnnotations_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 num_annotations; | |
annotation annotations[num_annotations]; | |
} | |
annotation { | |
u2 type_index; | |
u2 num_element_value_pairs; | |
ElementValuePair element_value_pairs[num_element_value_pairs]; | |
} | |
ElementValuePair { | |
u2 element_name_index; | |
element_value value; | |
} |
- type_index: CONSTANT_Utf8 类型的常量池的下标,注解的全限定名称。
- element_value_pairs: 注解中的元素。
- element_name_index: 元素名称,CONSTANT_Utf8 类型的常量池的下标。
- element_value: 参考下面。
# element_value
element_value { | |
u1 tag; | |
Value value; | |
} | |
union Value { | |
u2 const_value_index; | |
EnumConst enum_const_value; | |
u2 class_info_index; | |
annotation annotation_value; | |
ArrayValue array_value; | |
} | |
EnumConst { | |
u2 type_name_index; | |
u2 const_name_index; | |
} | |
ArrayValue { | |
u2 num_values; | |
element_value values[num_values]; | |
} |
tag
元素值对的类型
基本类型:
| 类型 | 描述 | 类型 | 描述 |
|---|---|---|---|
| B | byte | C | char |
| D | double | F | float |
| I | int | J | long |
| S | short | Z | boolean |
其它类型:
| 类型 | 描述 | 类型 | 描述 |
|---|---|---|---|
| s | String | e | enum constant |
| c | class | @ | annotation type |
| [ | array |
value
根据 tag 的不同取值,使用 value 中对应的字段。
const_value_index
如果 tag 是 B, C, D, F, I, J, S, Z, 或 s 其中一个,则 const_value_index 引用对应类型的常量池。
enum_const_value
如果 tag 是 e,则由 type_name_index 和 const_name_index 组成,记录的是枚举类型的元素。
- type_name_index: 枚举的类型全限定名,引用 CONSTANT_Utf8 类型的常量池下标。
- const_name_index: 枚举中定义的名称,引用 CONSTANT_Utf8 类型的常量池下标。
class_info_index
如果 tag 是 c,则记录的是 class,引用 CONSTANT_Utf8 类型的常量池下标,类的全限定名称;比如:V 代表 Void.class, Ljava/lang/Object; 代表 Object。
annotation_value
如果 tag 是 @,则记录的是注解,嵌套使用,参考这节的 element_value。
array_value
如果 tag 是 [,记录的是数组,由 num_values 和 values 组成。
- values: element_value,参考该节。
# RuntimeInvisibleAnnotations
位置:ClassFile, field_info, or method_info
与 RuntimeVisibleAnnotations 类似,这个是不可见的注解,调用 java 反射 API 时,获取不到。
java 中的 Retention 注解配置。
- RetentionPolicy.SOURCE: 源码可见,比如:@Override,编译之后则不存在。
- RetentionPolicy.CLASS: 留在 class 文件,但忽略不处理,即记录在 RuntimeInvisibleAnnotations。
- RetentionPolicy.RUNTIME: 通过反射机制可以获取,即存储在 RuntimeVisibleAnnotations。
RuntimeInvisibleAnnotations_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 num_annotations; | |
annotation annotations[num_annotations]; | |
} |
# RuntimeVisibleParameterAnnotations
位置:method_info
方法参数列表里的注解,通过反射机制可以获取到这部分注解信息。
RuntimeVisibleParameterAnnotations_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u1 num_parameters; | |
ParameterAnnotation parameter_annotations[num_parameters]; | |
} | |
ParameterAnnotation { | |
u2 num_annotations; | |
annotation annotations[num_annotations]; | |
} |
# RuntimeInvisibleParameterAnnotations
位置:method_info
方法参数列表里的注解,不能通过反射机制可以获取到这部分注解信息。
RuntimeInvisibleParameterAnnotations { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u1 num_parameters; | |
ParameterAnnotation parameter_annotations[num_parameters]; | |
} | |
ParameterAnnotation { | |
u2 num_annotations; | |
annotation annotations[num_annotations]; | |
} |
# AnnotationDefault
位置:method_info
注解类默认值。
AnnotationDefault_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
element_value default_value; | |
} |
# BootstrapMethods
位置:ClassFile
invokedynamic 指引的引导方法限定符。
BootstrapMethods_attribute { | |
u2 attribute_name_index; | |
u4 attribute_length; | |
u2 num_bootstrap_methods; | |
_bootstrap_method bootstrap_methods[num_bootstrap_methods]; | |
} | |
_bootstrap_method { | |
u2 bootstrap_method_ref; | |
u2 num_bootstrap_arguments; | |
u2 bootstrap_arguments[num_bootstrap_arguments]; | |
} |
bootstrap_method_ref
引用 CONSTANT_MethodHandle 类型的常量池下标
bootstrap_arguments
可以引用以下类型的常量:
- CONSTANT_String_info
- CONSTANT_Class_info
- CONSTANT_Integer_info
- CONSTANT_Long_info
- CONSTANT_Float_info
- CONSTANT_Double_info
- CONSTANT_MethodHandle_info
- CONSTANT_MethodType_info
# 字节码指令
指令:操作码(Opcode)+ 0 至多个操作数(Operands)
Opcode 表
# 加载和存储指令
# 将局部变量加载到操作栈
对应类型:
- (i, l, f, d, a)->(int, long, float, double, reference)
- (b, c, s)->(byte / boolean, char, short)
指令: iload,lload,fload,dload,aload
格式: <指令> <index>
操作数栈: ... --> ..., value
变化:
- 将本地变量数组下标 index 下的 value 压入操作数栈;
- 如果是 l 和 d 类型的指令,则 index 和 index + 1 都入栈,因为占 2 个 Slot。
描述: index 是本地变量数组 (local variable array) 的下标。
注意: aload 指令的 value 是 objectref,且不能用于 returnAddress 类型的加载;相反,存储指令 astore 可以存储该类型。
指令: iload_n,lload_n,fload_n,dload_n,aload_n
格式: <指令>
操作数栈: ... --> ..., value
变化:
- 将本地变量数组下标 n 下的 value 压入操作数栈。
- 如果是 l 和 d 类型的指令,则 n 和 n + 1 都入栈,因为占 2 个 Slot。
描述: 代表一组指令,n 可取的值为(0, 1, 2, 3);n 为 index,用 n 代替 index,隐式指定 index,不带操作数;例如 iload_0 和 iload 0 语义一样。
注意: aload_n 指令的 value 是 objectref,且不能用于 returnAddress 类型的加载;相反,存储指令 astore_n 可以存储该类型。
指令:
iaload,laload,faload,daload,aaload, baload,caload,saload
格式: <指令>
操作数栈: ..., arrayref, index --> ..., value
变化: 将操作数栈中的 arrayref 和 index 推出栈,然后将 arrayref 中 index 下的 value 压入操作数栈。
描述: 加载对应类型的数组下的值。
运行时异常:
- arrayref 是 null,抛 NullPointerException 异常;
- index 越界,抛 ArrayIndexOutOfBoundsException 异常。
# 2. 将一个数从操作数栈存储到局部变量表
指令: istore,lstore,fstore,dstore,astore
格式: <指令> <index>
操作数栈: ..., value --> ...
变化:
- 将操作数栈的 value 推出栈,然后设置本地变量数组下标 index 下的 value;
- 如果是 l 和 d 类型的指令,则设置 index 和 index+1 下标的值,因为占 2 个 Slot。
注意: astore 指令的 value 是 objectref,且可以存储 returnAddress 类型。
指令: istore_n,lstore_n,fstore_n,dstore_n,astore_n
格式: <指令>
操作数栈: ..., value --> ...
变化:
- 将操作数栈的 value 推出栈,然后设置本地变量数组下标 n 下的 value。
- 如果是 l 和 d 类型,则设置 n 和 n+1 下标的值,因为占 2 个 Slot。
描述: 代表一组指令,n 可取的值为(0, 1, 2, 3);n 为 index,用 n 代替 index,隐式指定 index,不带操作数;例如 istore_0 和 istore 0 语义一样。
注意: astore_n 指令的 value 是 objectref,且可以存储 returnAddress 类型。
指令:
iastore,lastore,fastore,dastore,aastore, bastore,castore,sastore
格式: <指令>
操作数栈: ..., arrayref, index, value --> ...
变化: 将操作数栈中的 arrayref, index 和 value 推出栈,然后设置 arrayref 中 index 下的 value。
描述: 存储到指定位置,对应类型的数组。
运行时异常:
- arrayref 是 null,抛 NullPointerException 异常;
- index 越界,抛 ArrayIndexOutOfBoundsException 异常;
- 如果是 aastore 指令,源类型与目标类型不兼容(不同类型,不是子类等),抛 ArrayStoreException 异常。
# 扩展局部变量表的访问索引的指令 wide
指令: wide
格式:
wide <opcode> indexbyte1 indexbyte2
wide iinc indexbyte1 indexbyte2 constbyte1 constbyte2
描述:
- opcode 是其中一个,iload, fload, aload, lload, dload, istore, fstore, astore, lstore, dstore, ret。
- iinc 是另外一个指令,格式不一样。
# 将一个常量加载到操作数栈中
指令: aconst_null
格式: aconst_null
操作数栈: ... --> ..., null
描述: Push null.
指令: bipush
格式: bipush byte
操作数栈: ... --> ..., value
描述: 将 byte 常量压入操作数栈;byte 会符合扩展成 int。
指令: sipush
格式: sipush byte1 byte2
操作数栈: ... --> ..., value
描述: 将 byte1 和 byte2 常量组合成 short((byte1 << 8) | byte2)一起压入操作数栈;short 会符合扩展成 int。
指令: ldc
格式: ldc index
操作数栈: ... --> ..., value
描述:
将 item 从运行时常量池中加载到操作数栈:
- int 或 float 类型常量,按对应类型加载数值到操作数栈;
- 字符串字面量的引用(reference to a string literal),将 reference 实例压入到栈中;
- 类的符号引用(symbolic reference to a class),将类的实例压入到栈中;
- method type,method handle:java.lang.invoke.MethodType,java.lang.invoke.MethodHandle 对应的实例压入栈中。
异常: 解析类的符号引用(symbolic reference to a class)、method type、method handle 时,可能会抛出对应解析异常。
指令: ldc_w
格式: ldc_w indexbyte1 indexbyte2
操作数栈: ... --> ..., value
描述: 扩展下标((indexbyte1 << 8) | indexbyte2),和 ldc 描述一致。
异常: 和 ldc 描述一致
指令: ldc2_w
格式: ldc2_w indexbyte1 indexbyte2
操作数栈: ... --> ..., value
描述: 扩展下标((indexbyte1 << 8) | indexbyte2),将 long 和 double 类型从运行时常量池中加载到操作数栈中。
指令: iconst_<i>
格式: iconst_<i>
操作数栈: ... --> ..., <i>
描述: <i> 可取(m1, 0, 1, 2, 3, 4, 5),m1 即 - 1;将 int 类型常量 - 1 ~ 5 其中一个压入到操作数栈中。
指令: lconst_<l>
格式: lconst_<l>
操作数栈: ... --> ..., <l>
描述: <l> 可取(0,1),将 long 类型常量 0 或 1 压入到操作数栈中。
指令: fconst_<f>
格式: fconst_<f>
操作数栈: ... --> ..., <f>
描述: <f> 可取(0,1,2),将 float 类型常量 0.0,1.0 或 2.0 压入到操作数栈中。
指令: dconst_<d>
格式: dconst_<d>
操作数栈: ... --> ..., <d>
描述: <d> 可取(0,1),将 double 类型常量 0.0 或 1.0 压入到操作数栈中。
# 运算指令
# 加法指令
指令: iadd,ladd,fadd,dadd
格式: <指令>;
操作数栈: ..., value1, value2 --> ..., result
描述: 将操作数栈中的两个值弹出栈然后相加,最后将结果压入栈中。int 和 long 的结果使用补码格式存储。
# 减法指令
指令: isub,lsub,fsub,dsub
格式: <指令>
操作数栈: ..., value1, value2 --> ..., result
描述: 将操作数栈中的两个值弹出栈然后相减,最后将结果压入栈中。int 和 long 的结果使用补码格式存储。
# 乘法指令
指令: imul,lmul,fmul,dmul
格式: <指令>
操作数栈: ..., value1, value2 --> ..., result
描述: 将操作数栈中的两个值弹出栈然后相乘,最后将结果压入栈中。int 和 long 的结果使用补码格式存储。
# 除法指令
指令: idiv,ldiv,fdiv,ddiv
格式: <指令>
操作数栈: ..., value1, value2 --> ..., result
描述: 将操作数栈中的两个值弹出栈然后相除,最后将结果压入栈中。
异常: int 或 long 类型导致 ArithmeticException(除 0 导致的异常)。
# 求余指令
指令: irem,lrem,frem,drem
格式: <指令>
操作数栈: ..., value1, value2 --> ..., result
描述: 将操作数栈中的两个值弹出栈然后求余,最后将结果压入栈中。
异常: int 或 long 类型导致 ArithmeticException(除数为 0 导致的异常)。
# 取反指令
指令: ineg,lneg,fneg,dneg
格式: <指令>
操作数栈: ..., value --> ..., result
描述: 将操作数栈中的值弹出栈然后取反,最后将结果压入栈中。
int 或 long 类型:-x = (~x)+1 (补码)。
# 移位指令
指令: ishl,lshl,ishr,lshr,iushr,lushr
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述:
- 左移,右移,无符号右移。
- int 类型,取 value2 的低 5 位(从 0 开始),value2 & 0x1f,移位范围 [0, 31]。
- long 类型,取 value2 的低 6 位(从 0 开始),value2 & 0x3f,移位范围 [0, 63]。
- 无符合右移:左边用 0 填充移动后的空位。
# 按位与指令
指令: iand,land
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述: 按位布尔与运算。
# 按位或指令
指令: ior,lor
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述: 按位布尔或运算。
# 按位异或指令
指令: ixor,lxor
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述: 按位布尔异或运算。
# 局部变量自增指令
指令: iinc
格式: iinc index const
操作数栈: 无变化
描述:
- index:当前帧的本地变量数组下标。
- const:下标对应的变量增加 const 数量。
- 支持 wide,宽字节指令;wide iinc indexbyte1 indexbyte2 constbyte1 constbyte2。
# 操作数栈管理指令
类别 1 计算类型(category 1 computational type)
类别 2 计算类型(category 2 computational type)
| Actual type | Computational type | Category |
|---|---|---|
| boolean | int | 1 |
| byte | int | 1 |
| char | int | 1 |
| short | int | 1 |
| int | int | 1 |
| float | float | 1 |
| reference | reference | 1 |
| returnAddress | returnAddress | 1 |
| long | long | 2 |
| double | double | 2 |
# 出栈
指令: pop,pop2
格式: <指令>
操作数栈:
..., value --> ... (pop、pop2 支持)
..., value1, value2 --> ... (pop2 支持)
描述:
- pop 弹出 1 个栈顶的值,仅类别 1 的计算类型使用;
- pop2 弹出 1 个或 2 个栈顶的值,个数的计算是根据类型计算的,long 和 double 占 2 个 Slot;类别 1 和 2 的计算类型使用。
# 复制
指令:
dup,dup_x1,dup_x2, dup2,dup2_x1,dup2_x2
格式: <指令>
描述: 复制栈顶的 1 或 2 个值;根据不同的类别计算类型,操作数栈也有多种形式。
操作数栈:
dup: value 是类别 1,复制栈顶 1 格的值
..., value --> ..., value, value
dup_x1: value1 和 value2 是类别 1;复制的值 value1 下移 1 格【不包括被复制的值的位置】
..., value2, value1 --> ..., value1, value2, value1
dup_x2:
- 全是类别 1;复制的值 value1 下移 2 格【不包括被复制的值的位置】
..., value3, value2, value1 --> ..., value1, value3, value2, value1
- value2 是类别 2,value1 是类别 1;复制的值 value1 下移 2 格【不包括被复制的值的位置】,因为 value2 直接占 2 格了
..., value2, value1 --> ..., value1, value2, value1
dup2:
- 全是类别 1;复制栈顶 2 格的值
..., value2, value1 --> ..., value2, value1, value2, value1
- 全是类别 2;复制栈顶 2 格的值,因为类别 2 占 2 格
..., value --> ..., value, value
dup2_x1:
- 全是类别 1;复制的值 value2 和 value1 下移 1 格【不包括被复制的值的位置】
..., value3, value2, value1 --> ..., value2, value1, value3, value2, value1
- value1 是类别 2;value2 是类别 1,复制的值 value1 下移 1 格【不包括被复制的值的位置】,因为 value1 占 2 格
..., value2, value1 --> ..., value1, value2, value1
dup2_x2:
- 全是类别 1;复制的值 value2 和 value1 下移 2 格【不包括被复制的值的位置】
..., value4, value3, value2, value1 --> ..., value2, value1, value4, value3, value2, value1
- value1 是类别 2,其它是类别 1;复制的值 value1 下移 2 格【不包括被复制的值的位置】,因为 value1 占 2 格
..., value3, value2, value1 --> ..., value1, value3, value2, value1
- value3 是类别 2,其它是类别 1;复制的值 value2 和 value1 下移 2 格【不包括被复制的值的位置】,因为 value3 占 2 格
..., value3, value2, value1 --> ..., value2, value1, value3, value2, value1
- 全是类别 2;复制的 value1 下移 2 格【不包括被复制的值的位置,因为 value1 和 value2 占 2 格
..., value2, value1 --> ..., value1, value2, value1
# 交换
指令: swap
格式: <指令>
操作数栈: ..., value2, value1 --> ..., value1, value2
描述:
交换栈顶的第 1 和第 2 格的值;仅支持类别 1 的操作,没有类别 2 的交换指令。
# 类型转换指令
指令:
i2l,i2f,i2d
l2i,l2f,l2d
f2i,f2l,f2d
d2i,d2l,d2f
格式: <指令>
操作数栈: ..., value --> ..., result
描述: i,l,f,d 相互转换。
指令: i2b,i2c,i2s
格式: <指令>
操作数栈: ..., value --> ..., result
描述: int 转换成 byte、char、short 类型。
# 比较运算指令
# 比较大小
指令: lcmp,fcmpl,fcmpg,dcmpl,dcmpg
格式: <指令>
操作数栈: ..., value1, value2 --> ..., result
描述:
比较 value1 与 value2 的大小:
- value1 > value2 => result = 1
- value1 = value2 => result = 0
- value1 < value2 => result = -1
fcmpl 与 fcmpg,dcmpl 与 dcmpg 的区别的是:如果 value1、value2 中任意 1 个是 NaN,则 fcmpl 和 dcmpl 的值是 - 1,而 fcmpg 和 dcmpg 的值是 1。
# 条件分支
指令: ifeq,ifne,iflt,ifge,ifgt,ifle
格式: <指令> branchbyte1 branchbyte2
操作数栈: ..., value --> ...
描述:
- value 与 0 比较,包括符号位,value 的类型是 int;
- 如果成立,则构造 16 位的无符号偏移量,即 (branchbyte1 << 8) | branchbyte2,然后跳到(address + 偏移量)的位置执行指令;
- 不成立则继续执行 if<cond> 后面的指令。
- eq => value = 0
- ne => value ≠ 0
- lt => value < 0
- le => value ≤ 0
- gt => value > 0
- ge => value ≥ 0
指令:
if_icmpeq,if_icmpne,if_icmplt,if_icmpge,if_icmpgt,if_icmple
if_acmpeq,if_acmpne
格式: <指令> branchbyte1 branchbyte2
操作数栈: ..., value1, value2 --> ...
描述:
- i 代表 value1 和 value2 是 int 类型;a 代表 value1 和 value2 是 reference 类型;
- value1 与 value2 比较;
- 如果成立,则构造 16 位的无符号偏移量,即 (branchbyte1 << 8) | branchbyte2,然后跳到(address + 偏移量)的位置执行指令;
- 不成立则继续执行 if_<cond> 后面的指令。
- eq => value1 = value2
- ne => value1 ≠ value2
- lt => value1 < value2
- le => value1 ≤ value2
- gt => value1 > value2
- ge => value1 ≥ value2
# 引用相关指令
# 创建类示例
指令: new
格式: new indexbyte1 indexbyte2
操作数栈: ... --> ..., objectref
描述:
- index = (indexbyte1 << 8) | indexbyte2;运行时常量池;
- 在垃圾回收堆内存中分配并初始化,然后 objectref 引用该内存,最后将 objectref 压入操作栈。
# 创建数组的指令
# 访问类字段
# 方法调用
# 获取数组长度
# 检查实例类型
# 异常处理
# 同步指令
# 其它指令
指令: nop
格式: nop
描述: 不做任何处理。