java号称编译一次,到处运行;其实真正可以到处运行的是java编译器编译之后的字节码文件同时jvm针对不同平台都做了实现;
除了平台无关性,字节码文件还有语言无关系;比如groovy,它用自己的编译器编译之后生成class文件同样可以运行在jvm之上。
其实我们自己也可以定义一种属于我们自己的语言,然后再写一个编译器,只要编译之后的文件符合class文件的规范,同样可以在jvm上运行。
盗图一张:
class字节码规范
Class 文件是一组以 8 位字节为基础单位的二进制流,其本质就是一张表,如下图所示,u1、u2、u4 表示 1 个字节、2 个字节、4 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 utf-8 编码构成的字符串值。_info 结尾的表示表,用以描述有层次关系的复合结构数据。
类型 | 名称 | 描述 |
---|---|---|
u4 | magic | 魔数,用以标记文件类型,固定值0xCAFEBABE |
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 | attribute_count | 属性的个数 |
attribute_info | attributes | 具体的属性内容,数量为 attributes_count |
常量池(Constant pool)
常量池是Class文件空间最大的数据项之一,长度不固定,主要包含两类:字面量(Literal)和符号引用(Symbolic References),字面量比较接近 Java 语言层面的常量概念,如文本字符串,声明为 final 的常量值等;符号引用这属于编译原理方面的概念,主要包括下面三类常量:
1 | - 类和接口全限定名(Full Qualified Name) |
Java 是在虚拟机加载 Class 文件的时候进行的动态链接,因此 Class 文件不会保存各个方法字段的最终内存布局信息,关于动态链接部分,将在下一篇文章Java系列(四)——类的加载与执行介绍。
常量池中的每一个数据项都有自己的类型,目前 Java 8一共有以下 14 种表结构数据支持不同的常量类型,具体结构可参见官方文档。
常量池中数据项类型 | 类型标志 | 类型描述 |
---|---|---|
CONSTANT_Utf8_info | 1 | UTF-8编码的Unicode字符串 |
CONSTANT_Integer_info | 3 | int类型字面值 |
CONSTANT_Float_info | 4 | float类型字面值 |
CONSTANT_Long_info | 5 | long类型字面值 |
CONSTANT_Double_info | 6 | double类型字面值 |
CONSTANT_Class_info | 7 | 对一个类或接口的符号引用 |
CONSTANT_String_info | 8 | String类型字面值 |
CONSTANT_Fieldref_info | 9 | 对一个字段的符号引用 |
CONSTANT_Methodref_info | 10 | 对一个类中声明的方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 对一个接口中声明的方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 对一个字段或方法的部分符号引用 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MethodType_info | 16 | 标示方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
可以使用 JDK 提供的 javap 工具,javap -verbose ClassName
的方式查看 Class 中的常量。
访问标志(Access flags)
2个字节代表,标示用于识别一些类或者接口层次的访问信息。
标识名 | 标识值 | 解释 |
---|---|---|
ACC_PUBLIC | 0x0001 | 声明为public;可以从包外部访问 |
ACC_FINAL | 0x0010 | 被声明为final;不允许子类修改 |
ACC_SUPER | 0x0020 | 当被invokespecial指令调用时,将特殊对待父类的方法 |
ACC_INTERFACE | 0x0200 | 接口标识符 |
ACC_ABSTRACT | 0x0400 | 声明为abstract;不能被实例化 |
ACC_SYNTHETIC | 0x1000 | 声明为synthetic;不存在于源代码,由编译器生成 |
ACC_ANNOTATION | 0x2000 | 声明为注释类型 |
ACC_ENUM | 0x4000 | 声明为枚举类型 |
类索引、父类索引和接口索引
类索引(this_class)和父类索引(super_class)都是一个 u2 的数据类型,而接口索引(Interfaces)是一组长度为interfaces_count 的 u2 类型的数据集合,它们的值大多存放的是对常量池中 CONSTANT_Class_info 的引用, Class 文件中由这三项数据来确定这个类的继承关系。
字段表(Field info)
字段表用于描述类或接口中声明的变量,格式如下:
1 | field_info { |
字段访问标识如下:(表中加粗项是字段独有的):
标识名 | 标识值 | 解释 |
---|---|---|
ACC_PUBLIC | 0x0001 | 声明为 public; 可以从包外部访问 |
ACC_PRIVATE | 0x0002 | 声明为 private; 只有定义的类可以访问 |
ACC_PROTECTED | 0x0004 | 声明为 protected;只有子类和相同package的类可访问 |
ACC_STATIC | 0x0008 | 声明为 static;属于类变量 |
ACC_FINAL | 0x0010 | 声明为 final; 对象构造后无法直接修改值 |
ACC_VOLATILE | 0x0040 | 声明为 volatile; 不会被缓存,直接刷新到主屏幕 |
ACC_TRANSIENT | 0x0080 | 声明为 transient; 不能被序列化 |
ACC_SYNTHETIC | 0x1000 | 声明为 synthetic; 不存在于源代码,由编译器生成 |
ACC_ENUM | 0x4000 | 声明为enum |
Java语法中,接口中的字段默认包含ACC_PUBLIC, ACC_STATIC, ACC_FINAL标识。ACC_FINAL,ACC_VOLATILE不能同时选择等规则。紧跟其后的name_index和descriptor_index是对常量池的引用,分别代表着字段的简单名和方法的描述符。
方法表(Method info)
方法表用于描述类或接口中声明的方法,格式如下:
1 | method_info { |
方法访问标识如下:(表中加粗项是方法独有的)
标识名 | 标识值 | 解释 |
---|---|---|
ACC_PUBLIC | 0x0001 | 声明为 public; 可以从包外部访问 |
ACC_PRIVATE | 0x0002 | 声明为 private; 只有定义的类可以访问 |
ACC_PROTECTED | 0x0004 | 声明为 protected;只有子类和相同package的类可访问 |
ACC_STATIC | 0x0008 | 声明为 static;属于类变量 |
ACC_FINAL | 0x0010 | 声明为 final; 不能被覆写 |
ACC_SYNCHRONIZED | 0x0020 | 声明为 synchronized; 同步锁包裹 |
ACC_BRIDGE | 0x0040 | 桥接方法, 由编译器生成 |
ACC_VARARGS | 0x0080 | 声明为 接收不定长参数 |
ACC_NATIVE | 0x0100 | 声明为 native; 由非Java语言来实现 |
ACC_ABSTRACT | 0x0400 | 声明为 abstract; 没有提供实现 |
ACC_STRICT | 0x0800 | 声明为 strictfp; 浮点模式是FP-strict |
ACC_SYNTHETIC | 0x1000 | 声明为 synthetic; 不存在于源代码,由编译器生成 |
- 对于方法里的Java代码,进过编译器编译成字节码指令后,存放在方法属性表集合中“code”的属性内。
- 当子类没有覆写父类方法,则方法集合中不会出现父类的方法信息。
- Java语言中重载方法,必须与原方法同名,且特征签名不同。特征签名是指方法中各个参数在常量池的字段符号引用的集合,不包括返回值。当时Class文件格式中,特征签名范围更广,允许方法名和特征签名都相同,但返回值不同的方法,合法地共存子啊同一个Class文件中。
属性表(Attribute info)
属性表的基本结构,不同类型的属性表以此为基础各不相同:
1 | attribute_info { |
属性表的限制相对宽松,不需要各个属性表有严格的顺序,只有不与已有的属性名重复,任何自定义的编译器都可以向属性表中写入自定义的属性信息,Java虚拟机运行时会忽略掉无法识别的属性。 关于虚拟机规范中预定义的属性,这里不展开讲了,列举几个常用的。
属性名 | 使用位置 | 解释 |
---|---|---|
Code | 方法表 | 方法体的内容 |
ConstantValue | 字段表 | final关键字定义的常量值 |
Deprecated | 类、方法表、字段表 | 声明为deprecated |
InnerClasses | 类文件 | 内部类的列表 |
LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 |
LocalVariableTable | Code属性 | 方法的局部变量描述 |
Signature | 类、方法表、字段表 | 用于支持泛型的方法签名,由于Java的泛型采用擦除法,避免类型信息被擦除后导致签名混乱,Signature记录相关信息 |
理解:
class文件其实就是一种协议或者说是规范,它有自己的格式。java编译器读取java文件,按照class文件的规范生产对应的class文件,jvm根据class文件规范读取class文件之后运行。
解读工具javap
我们自己根据字节码文件的规范,也可以读懂class文件。sun公司其实已经提供了读取class文件的工具javap。
定义类如下:
1 | public class Student{ |
运行javac之后运行javap
1 | kb-jaydeMacBook-Air:Desktop kb_jay$ javac Student.java |
字节码指令
tip:如下表(转自网络)
字节码 | 助记符 | 指令含义 |
---|---|---|
0x00 | nop | None |
0x01 | aconst_null | 将null推送至栈顶 |
0x02 | iconst_m1 | 将int型-1推送至栈顶 |
0x03 | iconst_0 | 将int型0推送至栈顶 |
0x04 | iconst_1 | 将int型1推送至栈顶 |
0x05 | iconst_2 | 将int型2推送至栈顶 |
0x06 | iconst_3 | 将int型3推送至栈顶 |
0x07 | iconst_4 | 将int型4推送至栈顶 |
0x08 | iconst_5 | 将int型5推送至栈顶 |
0x09 | lconst_0 | 将long型0推送至栈顶 |
0x0a | lconst_1 | 将long型1推送至栈顶 |
0x0b | fconst_0 | 将float型0推送至栈顶 |
0x0c | fconst_1 | 将float型1推送至栈顶 |
0x0d | fconst_2 | 将float型2推送至栈顶 |
0x0e | dconst_0 | 将double型0推送至栈顶 |
0x0f | dconst_1 | 将double型1推送至栈顶 |
0x10 | bipush | 将单字节的常量值(-128~127)推送至栈顶 |
0x11 | sipush | 将一个短整型常量(-32768~32767)推送至栈顶 |
0x12 | ldc | 将int,float或String型常量值从常量池中推送至栈顶 |
0x13 | ldc_w | 将int,float或String型常量值从常量池中推送至栈顶(宽索引) |
0x14 | ldc2_w | 将long或double型常量值从常量池中推送至栈顶(宽索引) |
0x15 | iload | 将指定的int型本地变量推送至栈顶 |
0x16 | lload | 将指定的long型本地变量推送至栈顶 |
0x17 | fload | 将指定的float型本地变量推送至栈顶 |
0x18 | dload | 将指定的double型本地变量推送至栈顶 |
0x19 | aload | 将指定的引用类型本地变量推送至栈顶 |
0x1a | iload_0 | 将第一个int型本地变量推送至栈顶 |
0x1b | iload_1 | 将第二个int型本地变量推送至栈顶 |
0x1c | iload_2 | 将第三个int型本地变量推送至栈顶 |
0x1d | iload_3 | 将第四个int型本地变量推送至栈顶 |
0x1e | lload_0 | 将第一个long型本地变量推送至栈顶 |
0x1f | lload_1 | 将第二个long型本地变量推送至栈顶 |
0x20 | lload_2 | 将第三个long型本地变量推送至栈顶 |
0x21 | lload_3 | 将第四个long型本地变量推送至栈顶 |
0x22 | fload_0 | 将第一个float型本地变量推送至栈顶 |
0x23 | fload_1 | 将第二个float型本地变量推送至栈顶 |
0x24 | fload_2 | 将第三个float型本地变量推送至栈顶 |
0x25 | fload_3 | 将第四个float型本地变量推送至栈顶 |
0x26 | dload_0 | 将第一个double型本地变量推送至栈顶 |
0x27 | dload_1 | 将第二个double型本地变量推送至栈顶 |
0x28 | dload_2 | 将第三个double型本地变量推送至栈顶 |
0x29 | dload_3 | 将第四个double型本地变量推送至栈顶 |
0x2a | aload_0 | 将第一个引用类型本地变量推送至栈顶 |
0x2b | aload_1 | 将第二个引用类型本地变量推送至栈顶 |
0x2c | aload_2 | 将第三个引用类型本地变量推送至栈顶 |
0x2d | aload_3 | 将第四个引用类型本地变量推送至栈顶 |
0x2e | iaload | 将int型数组指定索引的值推送至栈顶 |
0x2f | laload | 将long型数组指定索引的值推送至栈顶 |
0x30 | faload | 将float型数组指定索引的值推送至栈顶 |
0x31 | daload | 将double型数组指定索引的值推送至栈顶 |
0x32 | aaload | 将引用类型数组指定索引的值推送至栈顶 |
0x33 | baload | 将boolean或byte型数组指定索引的值推送至栈顶 |
0x34 | caload | 将char型数组指定索引的值推送至栈顶 |
0x35 | saload | 将short型数组指定索引的值推送至栈顶 |
0x36 | istore | 将栈顶int型数值存入指定本地变量 |
0x37 | lstore | 将栈顶long型数值存入指定本地变量 |
0x38 | fstore | 将栈顶float型数值存入指定本地变量 |
0x39 | dstore | 将栈顶double型数值存入指定本地变量 |
0x3a | astore | 将栈顶引用类型数值存入指定本地变量 |
0x3b | istore_0 | 将栈顶int型数值存入第一个本地变量 |
0x3c | istore_1 | 将栈顶int型数值存入第二个本地变量 |
0x3d | istore_2 | 将栈顶int型数值存入第三个本地变量 |
0x3e | istore_3 | 将栈顶int型数值存入第四个本地变量 |
0x3f | lstore_0 | 将栈顶long型数值存入第一个本地变量 |
0x40 | lstore_1 | 将栈顶long型数值存入第二个本地变量 |
0x41 | lstore_2 | 将栈顶long型数值存入第三个本地变量 |
0x42 | lstore_3 | 将栈顶long型数值存入第四个本地变量 |
0x43 | fstore_0 | 将栈顶float型数值存入第一个本地变量 |
0x44 | fstore_1 | 将栈顶float型数值存入第二个本地变量 |
0x45 | fstore_2 | 将栈顶float型数值存入第三个本地变量 |
0x46 | fstore_3 | 将栈顶float型数值存入第四个本地变量 |
0x47 | dstore_0 | 将栈顶double型数值存入第一个本地变量 |
0x48 | dstore_1 | 将栈顶double型数值存入第二个本地变量 |
0x49 | dstore_2 | 将栈顶double型数值存入第三个本地变量 |
0x4a | dstore_3 | 将栈顶double型数值存入第四个本地变量 |
0x4b | astore_0 | 将栈顶引用型数值存入第一个本地变量 |
0x4c | astore_1 | 将栈顶引用型数值存入第二个本地变量 |
0x4d | astore_2 | 将栈顶引用型数值存入第三个本地变量 |
0x4e | astore_3 | 将栈顶引用型数值存入第四个本地变量 |
0x4f | iastore | 将栈顶int型数值存入指定数组的指定索引位置 |
0x50 | lastore | 将栈顶long型数值存入指定数组的指定索引位置 |
0x51 | fastore | 将栈顶float型数值存入指定数组的指定索引位置 |
0x52 | dastore | 将栈顶double型数值存入指定数组的指定索引位置 |
0x53 | aastore | 将栈顶引用型数值存入指定数组的指定索引位置 |
0x54 | bastore | 将栈顶boolean或byte型数值存入指定数组的指定索引位置 |
0x55 | castore | 将栈顶char型数值存入指定数组的指定索引位置 |
0x56 | sastore | 将栈顶short型数值存入指定数组的指定索引位置 |
0x57 | pop | 将栈顶数值弹出(数值不能是long或double类型的) |
0x58 | pop2 | 将栈顶的一个(对于非long或double类型)或两个数值(对于非long或double的其他类型)弹出 |
0x59 | dup | 复制栈顶数值并将复制值压入栈顶 |
0x5a | dup_x1 | 复制栈顶数值并将两个复制值压入栈顶 |
0x5b | dup_x2 | 复制栈顶数值并将三个(或两个)复制值压入栈顶 |
0x5c | dup2 | 复制栈顶一个(对于long或double类型)或两个(对于非long或double的其他类型)数值并将复制值压入栈顶 |
0x5d | dup2_x1 | dup_x1指令的双倍版本 |
0x5e | dup2_x2 | dup_x2指令的双倍版本 |
0x5f | swap | 将栈顶最顶端的两个数值互换(数值不能是long或double类型) |
0x60 | iadd | 将栈顶两int型数值相加并将结果压入栈顶 |
0x61 | ladd | 将栈顶两long型数值相加并将结果压入栈顶 |
0x62 | fadd | 将栈顶两float型数值相加并将结果压入栈顶 |
0x63 | dadd | 将栈顶两double型数值相加并将结果压入栈顶 |
0x64 | isub | 将栈顶两int型数值相减并将结果压入栈顶 |
0x65 | lsub | 将栈顶两long型数值相减并将结果压入栈顶 |
0x66 | fsub | 将栈顶两float型数值相减并将结果压入栈顶 |
0x67 | dsub | 将栈顶两double型数值相减并将结果压入栈顶 |
0x68 | imul | 将栈顶两int型数值相乘并将结果压入栈顶 |
0x69 | lmul | 将栈顶两long型数值相乘并将结果压入栈顶 |
0x6a | fmul | 将栈顶两float型数值相乘并将结果压入栈顶 |
0x6b | dmul | 将栈顶两double型数值相乘并将结果压入栈顶 |
0x6c | idiv | 将栈顶两int型数值相除并将结果压入栈顶 |
0x6d | ldiv | 将栈顶两long型数值相除并将结果压入栈顶 |
0x6e | fdiv | 将栈顶两float型数值相除并将结果压入栈顶 |
0x6f | ddiv | 将栈顶两double型数值相除并将结果压入栈顶 |
0x70 | irem | 将栈顶两int型数值作取模运算并将结果压入栈顶 |
0x71 | lrem | 将栈顶两long型数值作取模运算并将结果压入栈顶 |
0x72 | frem | 将栈顶两float型数值作取模运算并将结果压入栈顶 |
0x73 | drem | 将栈顶两double型数值作取模运算并将结果压入栈顶 |
0x74 | ineg | 将栈顶int型数值取负并将结果压入栈顶 |
0x75 | lneg | 将栈顶long型数值取负并将结果压入栈顶 |
0x76 | fneg | 将栈顶float型数值取负并将结果压入栈顶 |
0x77 | dneg | 将栈顶double型数值取负并将结果压入栈顶 |
0x78 | ishl | 将int型数值左移指定位数并将结果压入栈顶 |
0x79 | lshl | 将long型数值左移指定位数并将结果压入栈顶 |
0x7a | ishr | 将int型数值右(带符号)移指定位数并将结果压入栈顶 |
0x7b | lshr | 将long型数值右(带符号)移指定位数并将结果压入栈顶 |
0x7c | iushr | 将int型数值右(无符号)移指定位数并将结果压入栈顶 |
0x7d | lushr | 将long型数值右(无符号)移指定位数并将结果压入栈顶 |
0x7e | iand | 将栈顶两int型数值”按位与”并将结果压入栈顶 |
0x7f | land | 将栈顶两long型数值”按位与”并将结果压入栈顶 |
0x80 | ior | 将栈顶两int型数值”按位或”并将结果压入栈顶 |
0x81 | lor | 将栈顶两long型数值”按位或”并将结果压入栈顶 |
0x82 | ixor | 将栈顶两int型数值”按位异或”并将结果压入栈顶 |
0x83 | lxor | 将栈顶两long型数值”按位异或”并将结果压入栈顶 |
0x84 | iinc | 将指定int型变量增加指定值(如i++, i–, i+=2等) |
0x85 | i2l | 将栈顶int型数值强制转换为long型数值并将结果压入栈顶 |
0x86 | i2f | 将栈顶int型数值强制转换为float型数值并将结果压入栈顶 |
0x87 | i2d | 将栈顶int型数值强制转换为double型数值并将结果压入栈顶 |
0x88 | l2i | 将栈顶long型数值强制转换为int型数值并将结果压入栈顶 |
0x89 | l2f | 将栈顶long型数值强制转换为float型数值并将结果压入栈顶 |
0x8a | l2d | 将栈顶long型数值强制转换为double型数值并将结果压入栈顶 |
0x8b | f2i | 将栈顶float型数值强制转换为int型数值并将结果压入栈顶 |
0x8c | f2l | 将栈顶float型数值强制转换为long型数值并将结果压入栈顶 |
0x8d | f2d | 将栈顶float型数值强制转换为double型数值并将结果压入栈顶 |
0x8e | d2i | 将栈顶double型数值强制转换为int型数值并将结果压入栈顶 |
0x8f | d2l | 将栈顶double型数值强制转换为long型数值并将结果压入栈顶 |
0x90 | d2f | 将栈顶double型数值强制转换为float型数值并将结果压入栈顶 |
0x91 | i2b | 将栈顶int型数值强制转换为byte型数值并将结果压入栈顶 |
0x92 | i2c | 将栈顶int型数值强制转换为char型数值并将结果压入栈顶 |
0x93 | i2s | 将栈顶int型数值强制转换为short型数值并将结果压入栈顶 |
0x94 | lcmp | 比较栈顶两long型数值大小, 并将结果(1, 0或-1)压入栈顶 |
0x95 | fcmpl | 比较栈顶两float型数值大小, 并将结果(1, 0或-1)压入栈顶; 当其中一个数值为NaN 时, 将-1压入栈顶 |
0x96 | fcmpg | 比较栈顶两float型数值大小, 并将结果(1, 0或-1)压入栈顶; 当其中一个数值为NaN 时, 将1压入栈顶 |
0x97 | dcmpl | 比较栈顶两double型数值大小, 并将结果(1, 0或-1)压入栈顶; 当其中一个数值为NaN 时, 将-1压入栈顶 |
0x98 | dcmpg | 比较栈顶两double型数值大小, 并将结果(1, 0或-1)压入栈顶; 当其中一个数值为NaN 时, 将1压入栈顶 |
0x99 | ifeq | 当栈顶int型数值等于0时跳转 |
0x9a | ifne | 当栈顶int型数值不等于0时跳转 |
0x9b | iflt | 当栈顶int型数值小于0时跳转 |
0x9c | ifge | 当栈顶int型数值大于等于0时跳转 |
0x9d | ifgt | 当栈顶int型数值大于0时跳转 |
0x9e | ifle | 当栈顶int型数值小于等于0时跳转 |
0x9f | if_icmpeq | 比较栈顶两int型数值大小, 当结果等于0时跳转 |
0xa0 | if_icmpne | 比较栈顶两int型数值大小, 当结果不等于0时跳转 |
0xa1 | if_icmplt | 比较栈顶两int型数值大小, 当结果小于0时跳转 |
0xa2 | if_icmpge | 比较栈顶两int型数值大小, 当结果大于等于0时跳转 |
0xa3 | if_icmpgt | 比较栈顶两int型数值大小, 当结果大于0时跳转 |
0xa4 | if_icmple | 比较栈顶两int型数值大小, 当结果小于等于0时跳转 |
0xa5 | if_acmpeq | 比较栈顶两引用型数值, 当结果相等时跳转 |
0xa6 | if_acmpne | 比较栈顶两引用型数值, 当结果不相等时跳转 |
0xa7 | goto | 无条件跳转 |
0xa8 | jsr | 跳转至指定的16位offset位置, 并将jsr的下一条指令地址压入栈顶 |
0xa9 | ret | 返回至本地变量指定的index的指令位置(一般与jsr或jsr_w联合使用) |
0xaa | tableswitch | 用于switch条件跳转, case值连续(可变长度指令) |
0xab | lookupswitch | 用于switch条件跳转, case值不连续(可变长度指令) |
0xac | ireturn | 从当前方法返回int |
0xad | lreturn | 从当前方法返回long |
0xae | freturn | 从当前方法返回float |
0xaf | dreturn | 从当前方法返回double |
0xb0 | areturn | 从当前方法返回对象引用 |
0xb1 | return | 从当前方法返回void |
0xb2 | getstatic | 获取指定类的静态域, 并将其压入栈顶 |
0xb3 | putstatic | 为指定类的静态域赋值 |
0xb4 | getfield | 获取指定类的实例域, 并将其压入栈顶 |
0xb5 | putfield | 为指定类的实例域赋值 |
0xb6 | invokevirtual | 调用实例方法 |
0xb7 | invokespecial | 调用超类构建方法, 实例初始化方法, 私有方法 |
0xb8 | invokestatic | 调用静态方法 |
0xb9 | invokeinterface | 调用接口方法 |
0xba | invokedynamic | 调用动态方法 |
0xbb | new | 创建一个对象, 并将其引用引用值压入栈顶 |
0xbc | newarray | 创建一个指定的原始类型(如int, float, char等)的数组, 并将其引用值压入栈顶 |
0xbd | anewarray | 创建一个引用型(如类, 接口, 数组)的数组, 并将其引用值压入栈顶 |
0xbe | arraylength | 获取数组的长度值并压入栈顶 |
0xbf | athrow | 将栈顶的异常抛出 |
0xc0 | checkcast | 检验类型转换, 检验未通过将抛出 ClassCastException |
0xc1 | instanceof | 检验对象是否是指定类的实际, 如果是将1压入栈顶, 否则将0压入栈顶 |
0xc2 | monitorenter | 获得对象的锁, 用于同步方法或同步块 |
0xc3 | monitorexit | 释放对象的锁, 用于同步方法或同步块 |
0xc4 | wide | 扩展本地变量的宽度 |
0xc5 | multianewarray | 创建指定类型和指定维度的多维数组(执行该指令时, 操作栈中必须包含各维度的长度值), 并将其引用压入栈顶 |
0xc6 | ifnull | 为null时跳转 |
0xc7 | ifnonnull | 不为null时跳转 |
0xc8 | goto_w | 无条件跳转(宽索引) |
0xc9 | jsr_w | 跳转至指定的32位offset位置, 并将jsr_w的下一条指令地址压入栈顶 |
参考:深入理解java虚拟机