Java类文件结构和字节码分析学习

前言

Java源程序被编译为class文件后,加载进虚拟机中,那么虚拟机是如何解析和分析class内容的呢?这里我们将以一个具体的java程序编译后的字节码流进行分析,学习Class类文件的结构。

1 Class类文件结构

Class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格排列,无任何分隔符

Class文件结构中有两种结构:

  • 无符号数。
  • 表。

1.1 无符号数属于基本数据类型:

以u1、u2、u4、u8来表示1个字节、2个字节、4个字节和8个字节的无符号数,可以用来表示数字、索引引用、数量值或者按照UTF-8编码的字符串值

1.2 表的说明如下:

表是由多个无符号数和其他表作为数据项构成的复合数据结构,习惯以_info结尾,Class文件本身就是一个表

1.3 Class文件格式

Class文件有如下表的数据项构成:

表1-1:Class文件格式
类型名称数量
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

当需要描述一组同一类型数据时通常会前置一个容器计数器,以便有效分割定位Class文件各个数据项。

2 Class类文件具体分析

2.1 java示例程序

示例程序如下:

public class ExampleClass implements Serializable, Cloneable {
    private String name;

    public String showName(String prefix){
        return prefix+name;
    }
}

在分析字节码之前我们先用javap工具-verbose参数生成测试代码的字节码内容:

Last modified 2022-4-24; size 768 bytes
  MD5 checksum 2cda1b2fa86b366fb17e80d44718cef7
  Compiled from "ExampleClass.java"
public class com.example.springmvc.asm.ExampleClass
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#26         // java/lang/Object."<init>":()V
   #2 = Class              #27            // java/lang/StringBuilder
   #3 = Methodref          #2.#26         // java/lang/StringBuilder."<init>":()V
   #4 = Methodref          #2.#28         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #5 = Fieldref           #7.#29         // com/example/springmvc/asm/ExampleClass.name:Ljava/lang/String;
   #6 = Methodref          #2.#30         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #7 = Class              #31            // com/example/springmvc/asm/ExampleClass
   #8 = Class              #32            // java/lang/Object
   #9 = Utf8               name
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lcom/example/springmvc/asm/ExampleClass;
  #18 = Utf8               showName
  #19 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #20 = Utf8               prefix
  #21 = Utf8               MethodParameters
  #22 = Utf8               SourceFile
  #23 = Utf8               ExampleClass.java
  #24 = Utf8               RuntimeVisibleAnnotations
  #25 = Utf8               Lorg/springframework/stereotype/Component;
  #26 = NameAndType        #11:#12        // "<init>":()V
  #27 = Utf8               java/lang/StringBuilder
  #28 = NameAndType        #33:#34        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #29 = NameAndType        #9:#10         // name:Ljava/lang/String;
  #30 = NameAndType        #35:#36        // toString:()Ljava/lang/String;
  #31 = Utf8               com/example/springmvc/asm/ExampleClass
  #32 = Utf8               java/lang/Object
  #33 = Utf8               append
  #34 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #35 = Utf8               toString
  #36 = Utf8               ()Ljava/lang/String;
{
  public com.example.springmvc.asm.ExampleClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/springmvc/asm/ExampleClass;

  public java.lang.String showName(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: aload_1
         8: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        11: aload_0
        12: getfield      #5                  // Field name:Ljava/lang/String;
        15: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        21: areturn
      LineNumberTable:
        line 15: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      22     0  this   Lcom/example/springmvc/asm/ExampleClass;
            0      22     1 prefix   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      prefix
}
SourceFile: "ExampleClass.java"
RuntimeVisibleAnnotations:
  0: #25()
PS C:\files\idea-workspace\unit2\springmvc\target\classes\com\example\springmvc\asm> javap -verbose .\ExampleClass.class
Classfile /C:/files/idea-workspace/unit2/springmvc/target/classes/com/example/springmvc/asm/ExampleClass.class
  Last modified 2022-4-24; size 738 bytes
  MD5 checksum 3183c52ca80cd8c3534265a7064f521b
  Compiled from "ExampleClass.java"
public class com.example.springmvc.asm.ExampleClass implements java.io.Serializable,java.lang.Cloneable
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#26         // java/lang/Object."<init>":()V
   #2 = Class              #27            // java/lang/StringBuilder
   #3 = Methodref          #2.#26         // java/lang/StringBuilder."<init>":()V
   #4 = Methodref          #2.#28         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #5 = Fieldref           #7.#29         // com/example/springmvc/asm/ExampleClass.name:Ljava/lang/String;
   #6 = Methodref          #2.#30         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #7 = Class              #31            // com/example/springmvc/asm/ExampleClass
   #8 = Class              #32            // java/lang/Object
   #9 = Class              #33            // java/io/Serializable
  #10 = Class              #34            // java/lang/Cloneable
  #11 = Utf8               name
  #12 = Utf8               Ljava/lang/String;
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               LocalVariableTable
  #18 = Utf8               this
  #19 = Utf8               Lcom/example/springmvc/asm/ExampleClass;
  #20 = Utf8               showName
  #21 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #22 = Utf8               prefix
  #23 = Utf8               MethodParameters
  #24 = Utf8               SourceFile
  #25 = Utf8               ExampleClass.java
  #26 = NameAndType        #13:#14        // "<init>":()V
  #27 = Utf8               java/lang/StringBuilder
  #28 = NameAndType        #35:#36        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #29 = NameAndType        #11:#12        // name:Ljava/lang/String;
  #30 = NameAndType        #37:#38        // toString:()Ljava/lang/String;
  #31 = Utf8               com/example/springmvc/asm/ExampleClass
  #32 = Utf8               java/lang/Object
  #33 = Utf8               java/io/Serializable
  #34 = Utf8               java/lang/Cloneable
  #35 = Utf8               append
  #36 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #37 = Utf8               toString
  #38 = Utf8               ()Ljava/lang/String;
            0       5     0  this   Lcom/example/springmvc/asm/ExampleClass;

  public java.lang.String showName(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: aload_1
         8: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        11: aload_0
        12: getfield      #5                  // Field name:Ljava/lang/String;
        15: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        21: areturn
      LineNumberTable:
        line 14: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      22     0  this   Lcom/example/springmvc/asm/ExampleClass;
            0      22     1 prefix   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      prefix
}
SourceFile: "ExampleClass.java"

该内容有利于我们后续进行分析对比。

2.2 魔数和Class文件版本

每个文件的头4字节称为魔数,唯一作用是确保这个文件是否是一个能被虚拟机接受的Class文件。

2.2.1 魔数

在java中这个魔数(magic)固定为0xCAFEBABE,查看字节码文件(以16进制打开):

头四个字节如下:
在这里插入图片描述

2.2.2 版本号

之后四个字节,前两个为次版本号(minor_version),后两个为主版本号(major_version),如下:

4-7字节如下:
在这里插入图片描述

0x34 = 52,因此该Class文件对应的java版本52.0是JDK 1.8 = 52

2.3 常量池

由表1-1可知,紧接着版本的是常量池:常量个数+常量项

从表中可知常量索引是:1~constant_pool_count-1
把索引0空出来是制定Class文件格式规范时,满足“某些指向常量池的数据在特定情况下不引用任何一个常量池项目”。
除了常量池,后续接口索引集合、字段表集合、方法表集合等都是从0开始。

2.3.1 常量池长度

查看对应位置字节码,常量池长度如下:

8-9字节
在这里插入图片描述

可见本测试demo有2*16+7-1=38个常量,参考上述javap反编译的内容可见一致。

2.3.2 常量池常量类型说明

2.3.2.1 常量池字面量和符号引用的类型转化描述

常量池主要存放两大类常量:字面量和符号引用。

字面量:文本字符串,final类型的常量。
符号引用包括下面几种:
 1. 类和接口全限定名。
 2. 字段名称和描述符。
 3. 方法的名称和描述符。

内部名称
主要用于类或接口类型,这些类型表示为具有内部名称的编译类。类的内部名称就是该类的全限定名,其中点被斜线取代,以Object为例:

Object全限定名为java.lang.Object,内部将表示为:java/lang/Object

类型描述
Java类型在编译类中将表示为类型描述符,具体对应如下表:

表2-1:java类型和编译后类型描述符对比
Java类型类型描述符
booleanZ
charC
byteB
shortS
intI
floatF
longJ
doubleD
ObjectLjava/lang/Object;
int[][I
Object[][][[Ljava/lang/Object;

可以看出对应java基本类型,都描述为单一大写字符,对于class类型将描述为它内部的类名称前边修饰为L末尾添加分隔符;,对于数组类型需要在数组类型的前边添加[,几维就添加几个。

方法描述

方法描述符就是一系列类型描述符表示参数和一个方法返回类型,具体示例如下表所示:

表2-2:源文件方法和编译后描述符对比
源文件方法描述编译后的方法描述符
void m(int i, float f)(IF)V,void返回使用V表示
int m(Object o)(Ljava/lang/Object;)I
int[] m(int i, String s)(ILjava/lang/String;)[I
Object m(int[] i)([I)Ljava/lang/Object;
2.3.2.2 常量项分类和具体结构

常量池中每一项都是一个表,总共有11中类型,其具体定义如下:

表2-3:常量项分类
类型标志描述
CONSTANT_Utf8_info1UTF-8编码字符串
CONSTANT_Integer_info3整形字面量
CONSTANT_Float_info4浮点型字面量
CONSTANT_Long_info5长整形字面量
CONSTANT_Double_info6双精度浮点型字面量
CONSTANT_Class_info7类或接口的引用符合
CONSTANT_String_info8字符串类型字面量
CONSTANT_Fieldref_info9字段的引用符合
CONSTANT_Methodref_info10类中方法的引用符合
CONSTANT_InterfaceMethodref_info11接口中方法的引用符号
CONSTANT_NameAndType_info12字段或方法的部分符号引用

表2-3中具体每种常量的结构如下:

表2-4:具体常量项结构说明
常量项目类型描述
CONSTANT_Utf8_infotagu1值为1
lengthu2utf8编码字符串占的字节数
bytesu1长度为length的utf8编码的字符串
CONSTANT_Intege_infotagu1值为3
bytesu4按照高位在前存储的int值
CONSTANT_Float_infotagu1值为4
bytesu4按照高位在前存储的float值
CONSTANT_Long_infotagu1值为5
bytesu8按照高位在前存储的long值
CONSTANT_Double_infotagu1值为6
bytesu8按照高位在前存储的double值
CONSTANT_Class_infotagu1值为7
indexu2指向全限定名常量项索引
CONSTANT_String_infotagu1值为8
indexu2指向字符串字面量索引
CONSTANT_Fieldref_infotagu1值为9
indexu2指向声明字段的类或接口描述符CONSTANT_Class_info的索引项
indexu2指向字段描述符CONSTANT_NameAndType_info的索引项
CONSTANT_Methodref_infotagu1值为10
indexu2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexu2指向名称和类型描述符CONSTANT_NameAndType_info的索引项
CONSTANT_InterfaceMethodref_infotagu1值为11
indexu2指向声明方法的类描述符CONSTANT_Class_info的索引项
indexu2指向名称和类型描述符CONSTANT_NameAndType_info的索引项
CONSTANT_NameAndType_infotagu1值为12
indexu2指向该字段或者方法名称常量项的索引项
indexu2指向该字段或方法描述符常量项的索引项

2.3.3 字节码常量分析

字节码第一个常量如下:
在这里插入图片描述

可见0x0a=10,对应为CONSTANT_Methodref_info,该常量后两个对应为声明类描述符索引和名称与类型描述符索引分别为8和26,结合javap内容可知对应索引项为:java/lang/Objec和()V

javap的对应内容如下:
在这里插入图片描述
第二个常量分析:
在这里插入图片描述

07对应类型为:CONSTANT_Class_info,后边2字节对应类全限定名的索引即:27,查看可知该项为:java/lang/StringBuilder

通过对上述两个常量项分析,我们知道后续的常量项都可以通过此类方法推断出来。分析时可以借助字节码分析工具javap。

2.4 访问标志

在常量池结束之后,紧跟两个字节是代表访问标志(access_flags),这个标志用于识别类或接口的访问信息,具体的标志位和标志含义定义如下表:

表2-5:访问标志说明
标志名称标志值含义
ACC_PUBLIC0x0001是否为public类型
ACC_FINAL0x0010是否被声明为final,只有类可以设置
ACC_SUPER0x0020是否允许使用invokespecial字节码指令,jdk1.2之后默认类这个字段为true
ACC_INTERFACE0x0200标识这是一个接口
ACC_ABSTRACT0x0400是否为abstract类型,对应接口和抽象类为true,其它为假
ACC_SYNTHETIC0x1000标识这个类并非用户代码产生
ACC_ANNOTATION0x2000标识这是一个注解
ACC_ENUM0x4000标识这是一个枚举

以测试demo为例,没有声明final和abstract,因此它的ACC_PUBLIC和ACC_SUPER应该为真,因此它的标志位应该是:

access_flags:0x0001|0x0020=0x0021

字节码如下:
在这里插入图片描述
与上述分析一致:地址0x00000220(值:0x0021)。

2.5 类索引、父类索引和接口索引集合

类索引和父类索引都是u2类型数据,指向常量池中具体索引,接口集合为:接口个数+各个接口索引。

以此分析字节码如下(地址:0x00000223):
在这里插入图片描述
根据上述分析类索引为0x0007第七项、父类索引0x0008第8项,接口索引:个数0x0002(索引分别为:第九项和第10项),根据javap分析内容如下:
在这里插入图片描述

2.6 字段表集合

字段表用于描述接口或者类中声明的变量。

field包括:类级变量和实例级变量,不包括方法内部声明的变量。

字段表的结构如下:

表2-6:字段表结构说明
类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

字段的access_flags与类的访问符类似,都是u2类型,具体定义如下表:

表2-7:字段访问标识
标志名称标志值含义
ACC_PUBLIC0x0001字段是否public
ACC_PRIVATE0x0002字段是否为private
ACC_PROTECTED0x0004字段是否为protected
ACC_STATIC0x0008字段是否为static
ACC_FINAL0x0010字段是否为final
ACC_VOLATILE0x0040字段是否为volatile
ACC_TRANSIENT0x0080字段是否为transient
ACC_SYNTHETIC0x1000字段是否由编译器自动产生
ACC_ENUM0x4000字段是否为enum

根据上述这些信息,我们分析字节码如下:
在这里插入图片描述

上述分别对应字段个数1个,之后为每个字段具体信息:访问符+名称索引+描述符索引+属性个数

对应测试代码private String name,

0x0002——ACC_PRIVATE
0x000b——索引11:name
0x000c——索引12:Ljava/lang/String;
0x0000——属性个数为0

2.7 方法表集合

和字段表类似,描述类定义的方法。
方法表结构定义如下:

表2-8:方法表结构说明
类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

方法表访问标志也与字段类似,定义如下:

表2-9:字段访问标识
标志名称标志值含义
ACC_PUBLIC0x0001方法是否public
ACC_PRIVATE0x0002方法是否为private
ACC_PROTECTED0x0004方法是否为protected
ACC_STATIC0x0008方法是否为static
ACC_FINAL0x0010方法是否为final
ACC_BRIDGE0x0040方法是否由编译器产生的桥接方法(由于泛型擦除,为了兼容1.5产生之前的字节码
ACC_VARARGST0x0080方法是否接受不定参数
ACC_NATIVE0x0100方法是否为native
ACC_ABSTRACT0x0400方法是否是抽象的
ACC_STRICT0x0800方法是否为strictfp
ACC_SYNTHETIC0x1000方法是否由编译器自动产生

继续以我们的测试demo进行分析(方法表入口地址为0x00000237):
在这里插入图片描述

0x0002:对应方法个数为2,构造器和showName。
下面对应第一个方法:<init>
0x0001:access_flags方法访问符即ACC_PUBLIC.
0x000d:名称索引13,对应为
0x000e:方法描述符索引14,对应为()V
0x0001:属性个数,也即是改方法有一个属性

2.8 属性表集合

在Class文件、字段表和方法表都可以出现属性。

虚拟机定义的属性有以下几种:

表2-10:虚拟机规范预定义的属性
属性名称使用位置含义
Code方法表java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量值
Deprecated类、方法表、字段表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
InnerClasses类文件内部类列表
LineNumberTableCode属性java源码的行号和字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
SourceFile类文件源文件名称
Synthetic类、方法表、字段表标识方法或字段为编译器自动生成。

属性表一般结构如下:

表2-11:属性表结构
类型名称数量
u2attribute_name_index1
u2attribute_length1
u1infoattribute_length

下面为具体属性的结构定义均符合表2-11的结构,下面是Code属性的结构:

表2-12:Code属性表结构
类型名称数量
u2attribute_name_index(常量索引固定为Code)1
u4attribute_length(固定为整个属性表长度-6字节)1
u2max_stack(操作数栈最大深度)1
u2max_locals(局部变量表所需的存储空间)1
u4code_length(字节码指令长度)1
u1code(具体的字节码指令)code_length
u2exception_table_length(异常表长度)1
exceptionexception_table(具体异常表)exception_table_length
u2attributes_count(属性长度)1
attribute_infoattribute(具体属性)attribute_count

继续以代码为例进行分析:
在这里插入图片描述

0x000f:对应属性名称索引15,也就是Code。
0x0000002f:对应属性长度也即是47,对应图中竖线位置,因为只有一个属性,所以后边是就另一个方法了。
0x0001:最大栈深度为1。
0x0001:局部变量表存储空间为1。
0x00000005:字节码指令长度为5。
2a、b7、00、01、b1是具体指令代码流:查表可知分别对应aload、invokespecial、return。
1. 2a代表:aload这里指将局部变量表slot0:this加载到栈顶,查看oracle-jvm该指令无后续操作数跟随,进入下一个指令
2. b7代表:invokespecial指令后边跟随两个字节,表示需要调用方法的常量池索引,后续00 01也就是第一项:init方法
3. b1:retrun代表返回void,后续无操作数
0x0000:异常表长度,这里没有抛出也就是0
0x0002:属性数量,这里为2,后边也就是具体属性信息

由于Code属性中的属性只有LineNumberTable和LocalVariableTable这里说明以下:
LineNumberTable结构定义如下

表2-13:LineNumberTable属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2line_number_table_length1
line_number_infolilne_number_tableline_number_table_length

line_number_info包括:start_pc和line_number两个u2类型数据。前者为字节码行号,后者为java源码行号。

LocalVariableTable结构定义如下

表2-14:LocalVariableTable属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2local_variable_table_length1
local_variable_infolocal_variable_tablelocal_variable_table_length

其中local_variable_info结构如下

表2-15:local_variable_info属性结构
类型名称数量
u2start_pc1
u2length1
u2name_index1
u2descriptor_index1
u2index1

start_pc:局部变量的生命周期开始的字节码偏移量。
length:其作用范围覆盖长度。和start_pc构成了局部变量的作用范围。
name_index和descriptor_index指向常量池,代表局部变量名称和描述符。
index:代表局部变量在栈帧局部变量表中的slot位置

下面是Code属性中唯一属性的分析:
第一个属性:
在这里插入图片描述

0x0010:属性名称索引,对应为常量池16,LineNumberTable
0x00000006:属性长度,对应为6。
0x0001:line_number_table_length对应为1,即只有一个。
0x0000-0x000a:line_number_info对应为:line10:0。

第二个属性:
在这里插入图片描述

0x0011:属性名索引,对应常量池17,LocalVariableTable
0x0000000c:属性长度,12。
0x0001:局部变量表长度1。
具体变量如下
0x0000:字节码偏移为0。
0x0005:作用范围覆盖长度为5。
0x0012:名字索引为18,对应为this。
0x0013:描述索引为19,对应为Lcom/example/springmvc/asm/ExampleClass;。
0x0000:index对应slot位置为0。

第一个方法javap内容截图如下:
在这里插入图片描述

对应内容与我们分析一致,至此第一个方法分析结束。

第二个方法分析与之相同,不在叙述,具体方法范围如下:
在这里插入图片描述

方法范围为(0x00000275~0x000002cc).
0x000002cd~0x000002da为:MethodParameters,本文不讨论。
0x000002db~0x000002e1为SourceFile类型。
0x000002e2为填充。

参考文献

[1]. 《深入理解Java虚拟机》
[2]. 《ASM 4.0 A Java bytecode engineering library》
[3]. 从一个class文件深入理解Java字节码结构

参考表

下表来自博主大佬四月葡萄总结:从一个class文件深入理解Java字节码结构).

字节码指令表
字节码助记符指令含义
0x00nop什么都不做
0x01aconst_null将null推送至栈顶
0x02iconst_m1将int型-1推送至栈顶
0x03iconst_0将int型0推送至栈顶
0x04iconst_1将int型1推送至栈顶
0x05iconst_2将int型2推送至栈顶
0x06iconst_3将int型3推送至栈顶
0x07iconst_4将int型4推送至栈顶
0x08iconst_5将int型5推送至栈顶
0x09lconst_0将long型0推送至栈顶
0x0alconst_1将long型1推送至栈顶
0x0bfconst_0将float型0推送至栈顶
0x0cfconst_1将float型1推送至栈顶
0x0dfconst_2将float型2推送至栈顶
0x0edconst_0将do le型0推送至栈顶
0x0fdconst_1将do le型1推送至栈顶
0x10bipush将单字节的常量值(-128~127)推送至栈顶
0x11sipush将一个短整型常量值(-32768~32767)推送至栈顶
0x12ldc将int, float或String型常量值从常量池中推送至栈顶
0x13ldc_w将int, float或String型常量值从常量池中推送至栈顶(宽索引)
0x14ldc2_w将long或do le型常量值从常量池中推送至栈顶(宽索引)
0x15iload将指定的int型本地变量
0x16lload将指定的long型本地变量
0x17fload将指定的float型本地变量
0x18dload将指定的do le型本地变量
0x19aload将指定的引用类型本地变量
0x1aiload_0将第一个int型本地变量
0x1biload_1将第二个int型本地变量
0x1ciload_2将第三个int型本地变量
0x1diload_3将第四个int型本地变量
0x1elload_0将第一个long型本地变量
0x1flload_1将第二个long型本地变量
0x20lload_2将第三个long型本地变量
0x21lload_3将第四个long型本地变量
0x22fload_0将第一个float型本地变量
0x23fload_1将第二个float型本地变量
0x24fload_2将第三个float型本地变量
0x25fload_3将第四个float型本地变量
0x26dload_0将第一个do le型本地变量
0x27dload_1将第二个do le型本地变量
0x28dload_2将第三个do le型本地变量
0x29dload_3将第四个do le型本地变量
0x2aaload_0将第一个引用类型本地变量
0x2baload_1将第二个引用类型本地变量
0x2caload_2将第三个引用类型本地变量
0x2daload_3将第四个引用类型本地变量
0x2eiaload将int型数组指定索引的值推送至栈顶
0x2flaload将long型数组指定索引的值推送至栈顶
0x30faload将float型数组指定索引的值推送至栈顶
0x31daload将do le型数组指定索引的值推送至栈顶
0x32aaload将引用型数组指定索引的值推送至栈顶
0x33baload将boolean或byte型数组指定索引的值推送至栈顶
0x34caload将char型数组指定索引的值推送至栈顶
0x35saload将short型数组指定索引的值推送至栈顶
0x36istore将栈顶int型数值存入指定本地变量
0x37lstore将栈顶long型数值存入指定本地变量
0x38fstore将栈顶float型数值存入指定本地变量
0x39dstore将栈顶do le型数值存入指定本地变量
0x3aastore将栈顶引用型数值存入指定本地变量
0x3bistore_0将栈顶int型数值存入第一个本地变量
0x3cistore_1将栈顶int型数值存入第二个本地变量
0x3distore_2将栈顶int型数值存入第三个本地变量
0x3eistore_3将栈顶int型数值存入第四个本地变量
0x3flstore_0将栈顶long型数值存入第一个本地变量
0x40lstore_1将栈顶long型数值存入第二个本地变量
0x41lstore_2将栈顶long型数值存入第三个本地变量
0x42lstore_3将栈顶long型数值存入第四个本地变量
0x43fstore_0将栈顶float型数值存入第一个本地变量
0x44fstore_1将栈顶float型数值存入第二个本地变量
0x45fstore_2将栈顶float型数值存入第三个本地变量
0x46fstore_3将栈顶float型数值存入第四个本地变量
0x47dstore_0将栈顶do le型数值存入第一个本地变量
0x48dstore_1将栈顶do le型数值存入第二个本地变量
0x49dstore_2将栈顶do le型数值存入第三个本地变量
0x4adstore_3将栈顶do le型数值存入第四个本地变量
0x4bastore_0将栈顶引用型数值存入第一个本地变量
0x4castore_1将栈顶引用型数值存入第二个本地变量
0x4dastore_2将栈顶引用型数值存入第三个本地变量
0x4eastore_3将栈顶引用型数值存入第四个本地变量
0x4fiastore将栈顶int型数值存入指定数组的指定索引位置
0x50lastore将栈顶long型数值存入指定数组的指定索引位置
0x51fastore将栈顶float型数值存入指定数组的指定索引位置
0x52dastore将栈顶do le型数值存入指定数组的指定索引位置
0x53aastore将栈顶引用型数值存入指定数组的指定索引位置
0x54bastore将栈顶boolean或byte型数值存入指定数组的指定索引位置
0x55castore将栈顶char型数值存入指定数组的指定索引位置
0x56sastore将栈顶short型数值存入指定数组的指定索引位置
0x57pop将栈顶数值弹出 (数值不能是long或do le类型的)
0x58pop2将栈顶的一个(long或do le类型的)或两个数值弹出(其它)
0x59dup复制栈顶数值并将复制值压入栈顶
0x5adup_x1复制栈顶数值并将两个复制值压入栈顶
0x5bdup_x2复制栈顶数值并将三个(或两个)复制值压入栈顶
0x5cdup2复制栈顶一个(long或do le类型的)或两个(其它)数值并将复制值压入栈顶
0x5ddup2_x1dup_x1 指令的双倍版本
0x5edup2_x2dup_x2 指令的双倍版本
0x5fswap将栈最顶端的两个数值互换(数值不能是long或do le类型的)
0x60iadd将栈顶两int型数值相加并将结果压入栈顶
0x61ladd将栈顶两long型数值相加并将结果压入栈顶
0x62fadd将栈顶两float型数值相加并将结果压入栈顶
0x63dadd将栈顶两do le型数值相加并将结果压入栈顶
0x64is将栈顶两int型数值相减并将结果压入栈顶
0x65ls将栈顶两long型数值相减并将结果压入栈顶
0x66fs将栈顶两float型数值相减并将结果压入栈顶
0x67ds将栈顶两do le型数值相减并将结果压入栈顶
0x68imul将栈顶两int型数值相乘并将结果压入栈顶
0x69lmul将栈顶两long型数值相乘并将结果压入栈顶
0x6afmul将栈顶两float型数值相乘并将结果压入栈顶
0x6bdmul将栈顶两do le型数值相乘并将结果压入栈顶
0x6cidiv将栈顶两int型数值相除并将结果压入栈顶
0x6dldiv将栈顶两long型数值相除并将结果压入栈顶
0x6efdiv将栈顶两float型数值相除并将结果压入栈顶
0x6fddiv将栈顶两do le型数值相除并将结果压入栈顶
0x70irem将栈顶两int型数值作取模运算并将结果压入栈顶
0x71lrem将栈顶两long型数值作取模运算并将结果压入栈顶
0x72frem将栈顶两float型数值作取模运算并将结果压入栈顶
0x73drem将栈顶两do le型数值作取模运算并将结果压入栈顶
0x74ineg将栈顶int型数值取负并将结果压入栈顶
0x75lneg将栈顶long型数值取负并将结果压入栈顶
0x76fneg将栈顶float型数值取负并将结果压入栈顶
0x77dneg将栈顶do le型数值取负并将结果压入栈顶
0x78ishl将int型数值左移位指定位数并将结果压入栈顶
0x79lshl将long型数值左移位指定位数并将结果压入栈顶
0x7aishr将int型数值右(符号)移位指定位数并将结果压入栈顶
0x7blshr将long型数值右(符号)移位指定位数并将结果压入栈顶
0x7ciushr将int型数值右(无符号)移位指定位数并将结果压入栈顶
0x7dlushr将long型数值右(无符号)移位指定位数并将结果压入栈顶
0x7eiand将栈顶两int型数值作“按位与”并将结果压入栈顶
0x7fland将栈顶两long型数值作“按位与”并将结果压入栈顶
0x80ior将栈顶两int型数值作“按位或”并将结果压入栈顶
0x81lor将栈顶两long型数值作“按位或”并将结果压入栈顶
0x82ixor将栈顶两int型数值作“按位异或”并将结果压入栈顶
0x83lxor将栈顶两long型数值作“按位异或”并将结果压入栈顶
0x84iinc将指定int型变量增加指定值(i++, i–, i+=2)
0x85i2l将栈顶int型数值强制转换成long型数值并将结果压入栈顶
0x86i2f将栈顶int型数值强制转换成float型数值并将结果压入栈顶
0x87i2d将栈顶int型数值强制转换成do le型数值并将结果压入栈顶
0x88l2i将栈顶long型数值强制转换成int型数值并将结果压入栈顶
0x89l2f将栈顶long型数值强制转换成float型数值并将结果压入栈顶
0x8al2d将栈顶long型数值强制转换成do le型数值并将结果压入栈顶
0x8bf2i将栈顶float型数值强制转换成int型数值并将结果压入栈顶
0x8cf2l将栈顶float型数值强制转换成long型数值并将结果压入栈顶
0x8df2d将栈顶float型数值强制转换成do le型数值并将结果压入栈顶
0x8ed2i将栈顶do le型数值强制转换成int型数值并将结果压入栈顶
0x8fd2l将栈顶do le型数值强制转换成long型数值并将结果压入栈顶
0x90d2f将栈顶do le型数值强制转换成float型数值并将结果压入栈顶
0x91i2b将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
0x92i2c将栈顶int型数值强制转换成char型数值并将结果压入栈顶
0x93i2s将栈顶int型数值强制转换成short型数值并将结果压入栈顶
0x94lcmp比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
0x95fcmpl比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x96fcmpg比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x97dcmpl比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x98dcmpg比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x99ifeq当栈顶int型数值等于0时跳转
0x9aifne当栈顶int型数值不等于0时跳转
0x9biflt当栈顶int型数值小于0时跳转
0x9cifge当栈顶int型数值大于等于0时跳转
0x9difgt当栈顶int型数值大于0时跳转
0x9eifle当栈顶int型数值小于等于0时跳转
0x9fif_icmpeq比较栈顶两int型数值大小,当结果等于0时跳转
0xa0if_icmpne比较栈顶两int型数值大小,当结果不等于0时跳转
0xa1if_icmplt比较栈顶两int型数值大小,当结果小于0时跳转
0xa2if_icmpge比较栈顶两int型数值大小,当结果大于等于0时跳转
0xa3if_icmpgt比较栈顶两int型数值大小,当结果大于0时跳转
0xa4if_icmple比较栈顶两int型数值大小,当结果小于等于0时跳转
0xa5if_acmpeq比较栈顶两引用型数值,当结果相等时跳转
0xa6if_acmpne比较栈顶两引用型数值,当结果不相等时跳转
0xa7goto无条件跳转
0xa8jsr跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶
0xa9ret返回至本地变量
0xaatableswitch用于switch条件跳转,case值连续(可变长度指令)
0xablookupswitch用于switch条件跳转,case值不连续(可变长度指令)
0xacireturn从当前方法返回int
0xadlreturn从当前方法返回long
0xaefreturn从当前方法返回float
0xafdreturn从当前方法返回do le
0xb0areturn从当前方法返回对象引用
0xb1return从当前方法返回void
0xb2getstatic获取指定类的静态域,并将其值压入栈顶
0xb3putstatic为指定的类的静态域赋值
0xb4getfield获取指定类的实例域,并将其值压入栈顶
0xb5putfield为指定的类的实例域赋值
0xb6invokevirtual调用实例方法
0xb7invokespecial调用超类构造方法,实例初始化方法,私有方法
0xb8invokestatic调用静态方法
0xb9invokeinterface调用接口方法
0xba无此指令
0xbbnew创建一个对象,并将其引用值压入栈顶
0xbcnewarray创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
0xbdanewarray创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
0xbearraylength获得数组的长度值并压入栈顶
0xbfathrow将栈顶的异常抛出
0xc0checkcast检验类型转换,检验未通过将抛出ClassCastException
0xc1instanceof检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
0xc2monitorenter获得对象的锁,用于同步方法或同步块
0xc3monitorexit释放对象的锁,用于同步方法或同步块
0xc4wide<待补充>
0xc5multianewarray创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶
0xc6ifnull为null时跳转
0xc7ifnonnull不为null时跳转
0xc8goto_w无条件跳转(宽索引)
0xc9jsr_w跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LamaxiyaFc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值