# 参考资料

  1. Chapter 4. The class File Format, oracle 官方技术规范
  2. Chapter 6. The Java Virtual Machine Instruction Set
  3. Chapter 7. Opcode Mnemonics by Opcode

# 无关性的基石

字节码(ByteCode)

jvm_language_independence

# Class 类文件的结构

查看类文件结构的命令

javap -verbose <类文件>

类型名称数量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count - 1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_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,实际数量是 constant_pool_count1constant\_pool\_count - 1

# 存储内容

字面量:

  • 文本字符串
  • 声明为 final 的常量值等

符号引用(Symbolic References):

  • 类和接口的全限定名(Fully Qualified Name)
  • 字段的名称和描述符(Descriptor)
  • 方法的名称和描述符

# 常量池格式

cp_info {
    u1 tag;
    u1 info[];
}

# tag 标记

类型标志描述
CONSTANT_Utf81UTF8 字符串
CONSTANT_Integer3整型
CONSTANT_Float4单精度浮点型
CONSTANT_Long5长整型
CONSTANT_Double6双精度浮点型
CONSTANT_Class7类或接口
CONSTANT_String8字符串
CONSTANT_Fieldref9类的字段
CONSTANT_Methodref10类的方法
CONSTANT_InterfaceMethodref11类的接口方法
CONSTANT_NameAndType12字段或方法
CONSTANT_MethodHandle15方法句柄
CONSTANT_MethodType16方法类型
CONSTANT_InvokeDynamic18动态方法调用点

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_PUBLIC0x0001public 类型
ACC_FINAL0x0010final 类型
ACC_SUPER0x0020使用 invokespecial 新指令语义
ACC_INTERFACE0x0200接口
ACC_ABSTRACT0x0400抽象类
ACC_SYNTHETIC0x1000表明由编译器产生
ACC_ANNOTATION0x2000注解
ACC_ENUM0x4000枚举

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_PUBLIC0x0001public 修饰符
ACC_PRIVATE0x0002private 修饰符
ACC_PROTECTED0x0004protected 修饰符
ACC_STATIC0x0008static 修饰符
ACC_FINAL0x0010final 修饰符
ACC_VOLATILE0x0040volatile 修饰符
ACC_TRANSIENT0x0080transient 修饰符
ACC_SYNTHETIC0x1000表明是由编译器生成
ACC_ENUM0x4000enum 类型

# 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_PUBLIC0x0001public 修饰符
ACC_PRIVATE0x0002private 修饰符
ACC_PROTECTED0x0004protected 修饰符
ACC_STATIC0x0008static 修饰符
ACC_FINAL0x0010final 修饰符
ACC_SYNCHRONIZED0x0020synchronized 修饰符
ACC_BRIDGE0x0040表明由编译器产生的桥接方法
ACC_VARARGS0x0080表明方法接收不定参数
ACC_NATIVE0x0100native 修饰符
ACC_ABSTRACT0x0400abstract 修饰符
ACC_STRICT0x0800strictfp 修饰符
ACC_SYNTHETIC0x1000表明由编译器生成

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 字节的数量

# 预定义的类文件属性集合

属性sinceclass file
ConstantValue1.0.245.3
Code1.0.245.3
StackMapTable650.0
Exceptions1.0.245.3
InnerClasses1.145.3
EnclosingMethod5.049.0
Synthetic1.145.3
Signature5.049.0
SourceFile1.0.245.3
SourceDebugExtension5.049.0
LineNumberTable1.0.245.3
LocalVariableTable1.0.245.3
LocalVariableTypeTable5.049.0
Deprecated1.145.3
RuntimeVisibleAnnotations5.049.0
RuntimeInvisibleAnnotations5.049.0
RuntimeVisibleParameterAnnotations5.049.0
RuntimeInvisibleParameterAnnotations5.049.0
AnnotationDefault5.049.0
BootstrapMethods751.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
引用常量池的下标,对应的内容的类型根据字段类型不同而不同,类型对应下面的表格。

字段类型常量类型
longCONSTANT_Long
floatCONSTANT_Float
doubleCONSTANT_Double
int, short, char, byte, booleanCONSTANT_Integer
StringCONSTANT_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_PUBLIC0x0001public 修饰符
ACC_PRIVATE0x0002private 修饰符
ACC_PROTECTED0x0004protected 修饰符
ACC_STATIC0x0008static 修饰符
ACC_FINAL0x0010final 修饰符
ACC_INTERFACE0x0200interface 类
ACC_ABSTRACT0x0400abstract 修饰符
ACC_SYNTHETIC0x1000表明是由编译器生成
ACC_ANNOTATION0x2000annotation 类型
ACC_ENUM0x4000enum 类型

# 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
元素值对的类型

基本类型:

类型描述类型描述
BbyteCchar
DdoubleFfloat
IintJlong
SshortZboolean

其它类型:

类型描述类型描述
sStringeenum constant
cclass@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
变化:

  1. 将本地变量数组下标 index 下的 value 压入操作数栈;
  2. 如果是 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
变化:

  1. 将本地变量数组下标 n 下的 value 压入操作数栈。
  2. 如果是 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 压入操作数栈。
描述: 加载对应类型的数组下的值。
运行时异常:

  1. arrayref 是 null,抛 NullPointerException 异常;
  2. index 越界,抛 ArrayIndexOutOfBoundsException 异常。

# 2. 将一个数从操作数栈存储到局部变量表

指令: istore,lstore,fstore,dstore,astore
格式: <指令> <index>
操作数栈: ..., value --> ...
变化:

  1. 将操作数栈的 value 推出栈,然后设置本地变量数组下标 index 下的 value;
  2. 如果是 l 和 d 类型的指令,则设置 index 和 index+1 下标的值,因为占 2 个 Slot。

注意: astore 指令的 value 是 objectref,且可以存储 returnAddress 类型。


指令: istore_n,lstore_n,fstore_n,dstore_n,astore_n
格式: <指令>
操作数栈: ..., value --> ...
变化:

  1. 将操作数栈的 value 推出栈,然后设置本地变量数组下标 n 下的 value。
  2. 如果是 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。
描述: 存储到指定位置,对应类型的数组。
运行时异常:

  1. arrayref 是 null,抛 NullPointerException 异常;
  2. index 越界,抛 ArrayIndexOutOfBoundsException 异常;
  3. 如果是 aastore 指令,源类型与目标类型不兼容(不同类型,不是子类等),抛 ArrayStoreException 异常。

# 扩展局部变量表的访问索引的指令 wide

指令: wide
格式:

wide <opcode> indexbyte1 indexbyte2
wide iinc indexbyte1 indexbyte2 constbyte1 constbyte2

描述:

  1. opcode 是其中一个,iload, fload, aload, lload, dload, istore, fstore, astore, lstore, dstore, ret。
  2. 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 从运行时常量池中加载到操作数栈:

  1. int 或 float 类型常量,按对应类型加载数值到操作数栈;
  2. 字符串字面量的引用(reference to a string literal),将 reference 实例压入到栈中;
  3. 类的符号引用(symbolic reference to a class),将类的实例压入到栈中;
  4. 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>
操作数栈: ... --> ..., &lt;d&gt;
描述: <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
描述:

  1. 左移,右移,无符号右移。
  2. int 类型,取 value2 的低 5 位(从 0 开始),value2 & 0x1f,移位范围 [0, 31]。
  3. long 类型,取 value2 的低 6 位(从 0 开始),value2 & 0x3f,移位范围 [0, 63]。
  4. 无符合右移:左边用 0 填充移动后的空位。

# 按位与指令

指令: iand,land
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述: 按位布尔与运算。

# 按位或指令

指令: ior,lor
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述: 按位布尔或运算。

# 按位异或指令

指令: ixor,lxor
格式: <指令>
操作数栈: ..., value1,value2 --> ..., result
描述: 按位布尔异或运算。

# 局部变量自增指令

指令: iinc
格式: iinc index const
操作数栈: 无变化
描述:

  1. index:当前帧的本地变量数组下标。
  2. const:下标对应的变量增加 const 数量。
  3. 支持 wide,宽字节指令;wide iinc indexbyte1 indexbyte2 constbyte1 constbyte2。

# 操作数栈管理指令

类别 1 计算类型(category 1 computational type)

类别 2 计算类型(category 2 computational type)

Actual typeComputational typeCategory
booleanint1
byteint1
charint1
shortint1
intint1
floatfloat1
referencereference1
returnAddressreturnAddress1
longlong2
doubledouble2

# 出栈

指令: pop,pop2
格式: <指令>
操作数栈:

..., value --> ... (pop、pop2 支持)
..., value1, value2 --> ... (pop2 支持)

描述:

  1. pop 弹出 1 个栈顶的值,仅类别 1 的计算类型使用;
  2. 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. 全是类别 1;复制的值 value1 下移 2 格【不包括被复制的值的位置】

    ..., value3, value2, value1 --> ..., value1, value3, value2, value1

  2. value2 是类别 2,value1 是类别 1;复制的值 value1 下移 2 格【不包括被复制的值的位置】,因为 value2 直接占 2 格了

    ..., value2, value1 --> ..., value1, value2, value1


dup2:

  1. 全是类别 1;复制栈顶 2 格的值

    ..., value2, value1 --> ..., value2, value1, value2, value1

  2. 全是类别 2;复制栈顶 2 格的值,因为类别 2 占 2 格

    ..., value --> ..., value, value


dup2_x1:

  1. 全是类别 1;复制的值 value2 和 value1 下移 1 格【不包括被复制的值的位置】

    ..., value3, value2, value1 --> ..., value2, value1, value3, value2, value1

  2. value1 是类别 2;value2 是类别 1,复制的值 value1 下移 1 格【不包括被复制的值的位置】,因为 value1 占 2 格

    ..., value2, value1 --> ..., value1, value2, value1


dup2_x2:

  1. 全是类别 1;复制的值 value2 和 value1 下移 2 格【不包括被复制的值的位置】

    ..., value4, value3, value2, value1 --> ..., value2, value1, value4, value3, value2, value1

  2. value1 是类别 2,其它是类别 1;复制的值 value1 下移 2 格【不包括被复制的值的位置】,因为 value1 占 2 格

    ..., value3, value2, value1 --> ..., value1, value3, value2, value1

  3. value3 是类别 2,其它是类别 1;复制的值 value2 和 value1 下移 2 格【不包括被复制的值的位置】,因为 value3 占 2 格

    ..., value3, value2, value1 --> ..., value2, value1, value3, value2, value1

  4. 全是类别 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 的大小:

  1. value1 > value2 => result = 1
  2. value1 = value2 => result = 0
  3. 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 --> ...
描述:

  1. value 与 0 比较,包括符号位,value 的类型是 int;
  2. 如果成立,则构造 16 位的无符号偏移量,即 (branchbyte1 << 8) | branchbyte2,然后跳到(address + 偏移量)的位置执行指令;
  3. 不成立则继续执行 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 --> ...
描述:

  1. i 代表 value1 和 value2 是 int 类型;a 代表 value1 和 value2 是 reference 类型;
  2. value1 与 value2 比较;
  3. 如果成立,则构造 16 位的无符号偏移量,即 (branchbyte1 << 8) | branchbyte2,然后跳到(address + 偏移量)的位置执行指令;
  4. 不成立则继续执行 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
描述:

  1. index = (indexbyte1 << 8) | indexbyte2;运行时常量池;
  2. 在垃圾回收堆内存中分配并初始化,然后 objectref 引用该内存,最后将 objectref 压入操作栈。

# 创建数组的指令

# 访问类字段

# 方法调用

# 获取数组长度

# 检查实例类型

# 异常处理

# 同步指令

# 其它指令

指令: nop
格式: nop
描述: 不做任何处理。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Cecil 微信支付

微信支付

Cecil 支付宝

支付宝

Cecil PayPal

PayPal