字节码初览

示例程序

/**
 * @author shaoming
 */
public class ByteCodeView {

    public static void main(String[] args) {
        System.out.println(factorial(5));
    }

    public static int factorial(int n) {
        if (n == 0) {
            return 1;
        }
        return n * factorial(n - 1);
    }
}

准备命令

示例只展示最基础的准备指

  • 编译指令
javac ByteCodeView.java
  • 输出输出字节码的附加信息
javap -verbose ByteCodeView > ByteCode.txt

字节码详解

字节码助记符请参考网络上的助记符表

Classfile /Users/shaoming/workspace/self/extension/src/cn/shellming/t0/ByteCodeView.class
  Last modified 2021-8-2; size 528 bytes
  MD5 checksum 825e7254ffc19ed5e1ec428c3604dac7
  Compiled from "ByteCodeView.java"
public class cn.shellming.t0.ByteCodeView // 类在工程内的类路径
  minor version: 0 // JDK 小版本号
  major version: 52 // JDK 大版本号 
  flags: ACC_PUBLIC, ACC_SUPER  // public限定类,当调用invokespecial时,需要特殊处理父类的方法
Constant pool: // 常量池
   #1 = Methodref          #6.#18         // java/lang/Object."<init>":()V
   #2 = Fieldref           #19.#20        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #5.#21         // cn/shellming/t0/ByteCodeView.factorial:(I)I
   #4 = Methodref          #22.#23        // java/io/PrintStream.println:(I)V
   #5 = Class              #24            // cn/shellming/t0/ByteCodeView
   #6 = Class              #25            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               factorial
  #14 = Utf8               (I)I
  #15 = Utf8               StackMapTable
  #16 = Utf8               SourceFile
  #17 = Utf8               ByteCodeView.java
  #18 = NameAndType        #7:#8          // "<init>":()V
  #19 = Class              #26            // java/lang/System
  #20 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #21 = NameAndType        #13:#14        // factorial:(I)I
  #22 = Class              #29            // java/io/PrintStream
  #23 = NameAndType        #30:#31        // println:(I)V
  #24 = Utf8               cn/shellming/t0/ByteCodeView
  #25 = Utf8               java/lang/Object
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
  #31 = Utf8               (I)V
{
  public cn.shellming.t0.ByteCodeView();
    descriptor: ()V
    flags: ACC_PUBLIC // public 方法
    Code: // 方法栈的大小为1,局部变量表的大小为1,入参数量为1 默认无参的构造方法怎么会有入参呢?其实Java语言有一个潜规则:在任何实例方法里面都可以通过this来访问到此方法所属的对象。Java只是把这种机制后推到编译阶段完成而已。所以,这里的1都是指this这个参数而已。
      stack=1, locals=1, args_size=1 
         0: aload_0 // 从局部变量0中装载引用类型值入栈
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V , 调用常量池中的 "#1"初始化父类的构造方法
         4: return  // 返回销毁方法栈
      LineNumberTable:
        line 6: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC // 一个public static 的方法
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream; 通过getstatic获取系统的输出流静态方法
         3: iconst_5   // 把int常量5入栈
         4: invokestatic  #3                  // 调用静态方法 factorial:(I)I 
         7: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        10: return
      LineNumberTable:
        line 9: 0
        line 10: 10

  public static int factorial(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: iload_0                       // 从局部变量0中装载引用类型值入栈 就是读入参5 [5]
         1: ifne          6               // 若栈顶int类型值不为0则跳转到6 [5]
         4: iconst_1                      // 定义一个常量1并压入栈中 [1]
         5: ireturn                       // 返回栈顶数据
         6: iload_0									// [5]
         7: iload_0						// [5, 5]
         8: iconst_1                      // [1, 5, 5]
         9: isub                          // [4, 5]
        10: invokestatic  #3              // 用栈顶数据调用 factorial:(I)I
        13: imul													// 将栈顶两int类型数相乘,结果入栈
        14: ireturn												// 返回栈顶数据
      LineNumberTable:
        line 13: 0
        line 14: 4
        line 16: 6
      StackMapTable: number_of_entries = 1
        frame_type = 6 /* same */
}
SourceFile: "ByteCodeView.java"