Java字节码你学习了吗?

228 阅读5分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

一、什么是字节码

  Java bytecode 由单字节(byte)的指令组成,理论上最多支持 256 个操作码(opcode)。实际上 Java 只使用了200左右的操作码, 还有一些操作码则保留给调试操作。

  根据指令的性质,主要分为四个大类:

  • 1、栈操作指令,包括与局部变量交互的指令
  • 2、程序流程控制指令
  • 3、对象操作指令,包括方法调用指令
  • 4、算术运算以及类型转换指令

1.1 通用栈操作指令如下

指令码操作码(助记符)描述(栈指操作数栈)
0x00nop空操作。
0x57pop从栈顶弹出一个字长的数据。
0x58pop2从栈顶弹出两个字长的数据。
0x59dup复制栈顶一个字长的数据,将复制后的数据压栈。
0x5adup_x1复制栈顶一个字长的数据,弹出栈顶两个字长数据,先将复制后的数据压栈,再将弹出的两个字长数据压栈。
0x5bdup_x2复制栈顶一个字长的数据,弹出栈顶三个字长的数据,将复制后的数据压栈,再将弹出的三个字长的数据压栈。
0x5cdup2复制栈顶两个字长的数据,将复制后的两个字长的数据压栈。
0x5ddup2_x1复制栈顶两个字长的数据,弹出栈顶三个字长的数据,将复制后的两个字长的数据压栈,再将弹出的三个字长的数据压栈。
0x5edup2_x2复制栈顶两个字长的数据,弹出栈顶四个字长的数据,将复制后的两个字长的数据压栈,再将弹出的四个字长的数据压栈。
0x5fswap交换栈顶两个字长的数据的位置。Java指令中没有提供以两个字长为单位的交换指令。

1.2 程序流程控制指令(部分指令)--条件跳转指令

指令码操作码(助记符)描述(栈指操作数栈)
0x99ifeq若栈顶int类型值为0则跳转。
0x9aifne若栈顶int类型值不为0则跳转。
0x9biflt若栈顶int类型值小于0则跳转。
0x9cifge若栈顶int类型值大于等于0则跳转。
0x9difgt若栈顶int类型值大于0则跳转。
0x9eifle若栈顶int类型值小于等于0则跳转。
0x9fif_icmpeq若栈顶两int类型值相等则跳转。
0xa0if_icmpne若栈顶两int类型值不相等则跳转。

1.3 对象操作指令

指令码操作码(助记符)描述(栈指操作数栈)
0xbbnew创建新的对象实例。
0xc0checkcast类型强转。
0xc1instanceof判断类型。
0xb4getfield获取对象字段的值。
0xb5putfield给对象字段赋值。
0xb2getstatic获取静态字段的值。
0xb3putstatic给静态字段赋值。

1.4 整型算术运算部分指令

指令码操作码(助记符)描述(栈指操作数栈)
0x60iadd将栈顶两int类型数相加,结果入栈。
0x64isub将栈顶两int类型数相减,结果入栈。
0x68imul将栈顶两int类型数相乘,结果入栈。
0x6cidiv将栈顶两int类型数相除,结果入栈。
0x70irem将栈顶两int类型数取模,结果入栈。
0x74ineg将栈顶int类型值取负,结果入栈。

二、案例

2.1 简单字节码案例分析

1、源码

javac ByteCode.java   //用jdk自带的工具javac 编译源代码

public class ByteCode {
    public static void main(String[] args) {
        ByteCode obj = new ByteCode();
    }
}

2、查看字节码

javap的用法格式如下:javap <options> <classes> javap 参数如下:

  • -help --help -? 输出此用法消息
  • -version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
  • -v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
  • -l 输出行号和本地变量表
  • -public 仅显示公共类和成员
  • -protected 显示受保护的/公共类和成员
  • -package 显示程序包/受保护的/公共类 和成员 (默认)
  • -p -private 显示所有类和成员
  • -c 对代码进行反汇编
  • -s 输出内部类型签名
  • -sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
  • -constants 显示静态最终常量
  • -classpath 指定查找用户类文件的位置
  • -bootclasspath 覆盖引导类文件的位置

javap -c ByteCode   //用jdk自带的工具javap 反编译字节码

public class jvm.week1.ByteCode {
  public jvm.week1.ByteCode();
    Code:
       0: aload_0  //将局部变量0加载到操作数栈
       1: invokespecial #1  // Method java/lang/Object."<init>":()V 调用超类构造方法,实例初始化方法,私有方法
       4: return //void函数返回

  public static void main(java.lang.String[]);
    Code:
       0: new           #2  // class jvm/week1/ByteCode 创建一个对象,并将其引用值压入栈顶
       3: dup // 复制栈顶数值并将复制值压入栈顶
       4: invokespecial #3  // Method "<init>":()V 调用超类构造方法,实例初始化方法,私有方法
       7: astore_1 //将栈顶引用型数值存入第二个本地变量
       8: return
}