Java 字节码阅读

很多时候我们通过代码无法了解字节码执行过程,比如Try Catch Finally的执行过程,只有通过debug或者阅读字节码才能搞懂JVM是如何编译和设计执行流程的。

public class TestTryCatch {
    private String feild = "this is class variables";
    public static String staticFeild = "this is class static variables";
    public static final String staticFinalFeild = "this is class static final variables";
    public volatile String volatileFeild = "this is class volatile variables";
    static {
        System.out.println("from the class static block");
    }
    {
        System.out.println("from the class  block");
    }

    public TestTryCatch() {
    }

    public TestTryCatch(String feild) {
        this.feild = feild;
    }

    public static String staticMethod() {
        int a = 1;
        int b = 10;
        a = b;
        return "from static method";
    }

    public synchronized void syncMethod() {
        System.out.println("from sync method ");
    }

    public void syncBlock() {
        synchronized (this) {
            System.out.println("from sync block");
        }
    }

    public String main(String[] args) {
        try {
            staticMethod();
            syncBlock();
            return "from try";
        } catch (Exception e) {
            e.printStackTrace();
            return "from catch";
        } finally {
            return "from finally";
        }
    }
}

可以通过$JDKPath$\bin\javap -c -verbose $FileClass$来编译Java文件,也可以看我的另一篇博客,通过IDEA来查看https://www.jianshu.com/p/738de4e519b6
编译成字节码之后:
分析在后面,比较长,可以直接划过,回头再看

"/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home\bin\javap" -c -verbose com.wei.sample.clazz.TestTryCatch
Classfile /Users/shuxin.wei/Documents/icourtCode/sample/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/wei/sample/clazz/TestTryCatch.class
  Last modified 2019-5-16; size 1901 bytes
  MD5 checksum 00ed76899d96d428782d4d128839aa9f
  Compiled from "TestTryCatch.java"
public class com.wei.sample.clazz.TestTryCatch
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #22.#59        // java/lang/Object."<init>":()V
   #2 = String             #60            // this is class variables
   #3 = Fieldref           #21.#61        // com/wei/sample/clazz/TestTryCatch.feild:Ljava/lang/String;
   #4 = String             #62            // this is class volatile variables
   #5 = Fieldref           #21.#63        // com/wei/sample/clazz/TestTryCatch.volatileFeild:Ljava/lang/String;
   #6 = Fieldref           #64.#65        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = String             #66            // from the class  block
   #8 = Methodref          #67.#68        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = String             #69            // from static method
  #10 = String             #70            // from sync method
  #11 = String             #71            // from sync block
  #12 = Methodref          #21.#72        // com/wei/sample/clazz/TestTryCatch.staticMethod:()Ljava/lang/String;
  #13 = String             #73            // from try
  #14 = String             #74            // from finally
  #15 = Class              #75            // java/lang/Exception
  #16 = Methodref          #15.#76        // java/lang/Exception.printStackTrace:()V
  #17 = String             #77            // from catch
  #18 = String             #78            // this is class static variables
  #19 = Fieldref           #21.#79        // com/wei/sample/clazz/TestTryCatch.staticFeild:Ljava/lang/String;
  #20 = String             #80            // from the class static block
  #21 = Class              #81            // com/wei/sample/clazz/TestTryCatch
  #22 = Class              #82            // java/lang/Object
  #23 = Utf8               feild
  #24 = Utf8               Ljava/lang/String;
  #25 = Utf8               staticFeild
  #26 = Utf8               staticFinalFeild
  #27 = Utf8               ConstantValue
  #28 = String             #83            // this is class static final variables
  #29 = Utf8               volatileFeild
  #30 = Utf8               <init>
  #31 = Utf8               ()V
  #32 = Utf8               Code
  #33 = Utf8               LineNumberTable
  #34 = Utf8               LocalVariableTable
  #35 = Utf8               this
  #36 = Utf8               Lcom/wei/sample/clazz/TestTryCatch;
  #37 = Utf8               (Ljava/lang/String;)V
  #38 = Utf8               staticMethod
  #39 = Utf8               ()Ljava/lang/String;
  #40 = Utf8               a
  #41 = Utf8               I
  #42 = Utf8               b
  #43 = Utf8               syncMethod
  #44 = Utf8               syncBlock
  #45 = Utf8               StackMapTable
  #46 = Class              #81            // com/wei/sample/clazz/TestTryCatch
  #47 = Class              #82            // java/lang/Object
  #48 = Class              #84            // java/lang/Throwable
  #49 = Utf8               main
  #50 = Utf8               ([Ljava/lang/String;)Ljava/lang/String;
  #51 = Utf8               e
  #52 = Utf8               Ljava/lang/Exception;
  #53 = Utf8               args
  #54 = Utf8               [Ljava/lang/String;
  #55 = Class              #75            // java/lang/Exception
  #56 = Utf8               <clinit>
  #57 = Utf8               SourceFile
  #58 = Utf8               TestTryCatch.java
  #59 = NameAndType        #30:#31        // "<init>":()V
  #60 = Utf8               this is class variables
  #61 = NameAndType        #23:#24        // feild:Ljava/lang/String;
  #62 = Utf8               this is class volatile variables
  #63 = NameAndType        #29:#24        // volatileFeild:Ljava/lang/String;
  #64 = Class              #85            // java/lang/System
  #65 = NameAndType        #86:#87        // out:Ljava/io/PrintStream;
  #66 = Utf8               from the class  block
  #67 = Class              #88            // java/io/PrintStream
  #68 = NameAndType        #89:#37        // println:(Ljava/lang/String;)V
  #69 = Utf8               from static method
  #70 = Utf8               from sync method
  #71 = Utf8               from sync block
  #72 = NameAndType        #38:#39        // staticMethod:()Ljava/lang/String;
  #73 = Utf8               from try
  #74 = Utf8               from finally
  #75 = Utf8               java/lang/Exception
  #76 = NameAndType        #90:#31        // printStackTrace:()V
  #77 = Utf8               from catch
  #78 = Utf8               this is class static variables
  #79 = NameAndType        #25:#24        // staticFeild:Ljava/lang/String;
  #80 = Utf8               from the class static block
  #81 = Utf8               com/wei/sample/clazz/TestTryCatch
  #82 = Utf8               java/lang/Object
  #83 = Utf8               this is class static final variables
  #84 = Utf8               java/lang/Throwable
  #85 = Utf8               java/lang/System
  #86 = Utf8               out
  #87 = Utf8               Ljava/io/PrintStream;
  #88 = Utf8               java/io/PrintStream
  #89 = Utf8               println
  #90 = Utf8               printStackTrace
{
  public static java.lang.String staticFeild;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC

  public static final java.lang.String staticFinalFeild;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String this is class static final variables

  public volatile java.lang.String volatileFeild;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_VOLATILE

  public com.wei.sample.clazz.TestTryCatch();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String this is class variables
         7: putfield      #3                  // Field feild:Ljava/lang/String;
        10: aload_0
        11: ldc           #4                  // String this is class volatile variables
        13: putfield      #5                  // Field volatileFeild:Ljava/lang/String;
        16: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        19: ldc           #7                  // String from the class  block
        21: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        24: return
      LineNumberTable:
        line 24: 0
        line 11: 4
        line 14: 10
        line 21: 16
        line 25: 24
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      25     0  this   Lcom/wei/sample/clazz/TestTryCatch;

  public com.wei.sample.clazz.TestTryCatch(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String this is class variables
         7: putfield      #3                  // Field feild:Ljava/lang/String;
        10: aload_0
        11: ldc           #4                  // String this is class volatile variables
        13: putfield      #5                  // Field volatileFeild:Ljava/lang/String;
        16: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        19: ldc           #7                  // String from the class  block
        21: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        24: aload_0
        25: aload_1
        26: putfield      #3                  // Field feild:Ljava/lang/String;
        29: return
      LineNumberTable:
        line 27: 0
        line 11: 4
        line 14: 10
        line 21: 16
        line 28: 24
        line 29: 29
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      30     0  this   Lcom/wei/sample/clazz/TestTryCatch;
            0      30     1 feild   Ljava/lang/String;

  public static java.lang.String staticMethod();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=0
         0: iconst_1
         1: istore_0
         2: bipush        10
         4: istore_1
         5: iload_1
         6: istore_0
         7: ldc           #9                  // String from static method
         9: areturn
      LineNumberTable:
        line 32: 0
        line 33: 2
        line 34: 5
        line 35: 7
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            2       8     0     a   I
            5       5     1     b   I

  public synchronized void syncMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #10                 // String from sync method
         5: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 39: 0
        line 40: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/wei/sample/clazz/TestTryCatch;

  public void syncBlock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #11                 // String from sync block
         9: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
      LineNumberTable:
        line 43: 0
        line 44: 4
        line 45: 12
        line 46: 22
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  this   Lcom/wei/sample/clazz/TestTryCatch;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 17
          locals = [ class com/wei/sample/clazz/TestTryCatch, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public java.lang.String main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=5, args_size=2
         0: invokestatic  #12                 // Method staticMethod:()Ljava/lang/String;
         3: pop
         4: ldc           #13                 // String from try
         6: astore_2
         7: ldc           #14                 // String from finally
         9: areturn
        10: astore_2
        11: aload_2
        12: invokevirtual #16                 // Method java/lang/Exception.printStackTrace:()V
        15: ldc           #17                 // String from catch
        17: astore_3
        18: ldc           #14                 // String from finally
        20: areturn
        21: astore        4
        23: ldc           #14                 // String from finally
        25: areturn
      Exception table:
         from    to  target type
             0     7    10   Class java/lang/Exception
             0     7    21   any
            10    18    21   any
            21    23    21   any
      LineNumberTable:
        line 50: 0
        line 51: 4
        line 56: 7
        line 52: 10
        line 53: 11
        line 54: 15
        line 56: 18
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           11      10     2     e   Ljava/lang/Exception;
            0      26     0  this   Lcom/wei/sample/clazz/TestTryCatch;
            0      26     1  args   [Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/lang/Exception ]
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: ldc           #18                 // String this is class static variables
         2: putstatic     #19                 // Field staticFeild:Ljava/lang/String;
         5: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #20                 // String from the class static block
        10: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: return
      LineNumberTable:
        line 12: 0
        line 17: 5
        line 18: 13
}
SourceFile: "TestTryCatch.java"
Process finished with exit code 0

类信息和常量池

Classfile /Users/shuxin.wei/Documents/icourtCode/sample/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/wei/sample/clazz/TestTryCatch.class
  Last modified 2019-5-16; size 1901 bytes
  MD5 checksum 00ed76899d96d428782d4d128839aa9f
  Compiled from "TestTryCatch.java"
public class com.wei.sample.clazz.TestTryCatch
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #22.#59        // java/lang/Object."<init>":()V
   #2 = String             #60            // this is class variables

方法编译成字节码分析,以main方法为例

方法描述
public java.lang.String main(java.lang.String[]);
descriptor:方法参数和返回值描述
descriptor: ([Ljava/lang/String;)Ljava/lang/String;
flags:方法属性描述
 flags: ACC_PUBLIC  ACC_STATIC  ACC_SYNCHONIZE
code:方法栈描述
 Code:
      stack=1, locals=5, args_size=2
         0: invokestatic  #12                 // Method staticMethod:()Ljava/lang/String;
         3: pop
         4: ldc           #13                 // String from try
         6: astore_2
         7: ldc           #14                 // String from finally
         9: areturn
        10: astore_2
        11: aload_2
        12: invokevirtual #16                 // Method java/lang/Exception.printStackTrace:()V
        15: ldc           #17                 // String from catch
        17: astore_3
        18: ldc           #14                 // String from finally
        20: areturn
        21: astore        4
        23: ldc           #14                 // String from finally
        25: areturn

stack:栈深度
locals:本地变量个数
args_size:

指令含义
aload_0这个操作码是aload格式操作码中的一个。它们用来把对象引用加载到操作码栈。 表示正在被访问的局部变量数组的位置,但只能是0、1、2、3 中的一个。还有一些其它类似的操作码用来载入非对象引用的数据,如iload, lload, float 和 dload。其中 i 表示 int,l 表示 long,f 表示 float,d 表示 double。局部变量数组位置大于 3 的局部变量可以用 iload, lload, float, dload 和 aload 载入。这些操作码都只需要一个操作数,即数组中的位置
astore_2将引用数据类型赋值后存储在变量表中的下标3位置
bipush变量压栈:当int取值-1~ 5采用iconst指令,取值-128~ 127采用bipush指令,取值-32768~ 32767采用sipush指令,取值-2147483648~ 2147483647采用 ldc 指令
ldc这个操作码用来将常量从运行时常量池压栈到操作数栈
getstatic这个操作码用来把一个静态变量从运行时常量池的静态变量列表中压栈到操作数栈
invokespecial, invokevirtual这些操作码属于一组函数调用的操作码,包括:invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual。在这个 class 文件中,invokespecial 和 invokevirutal 两个指令都用到了,两者的区别是,invokevirutal 指令调用一个对象的实例方法,invokespecial 指令调用实例初始化方法、私有方法、父类方法。
return这个操作码属于ireturn、lreturn、freturn、dreturn、areturn 和 return 操作码组。每个操作码返回一种类型的返回值,其中 i 表示 int,l 表示 long,f 表示 float,d 表示 double,a 表示 对象引用。没有前缀类型字母的 return 表示返回 void
Exception table:异常跳转表
 Exception table:
         from    to  target type
             0     7    10   Class java/lang/Exception
             0     7    21   any
            10    18    21   any
            21    23    21   any

从异常表中,可以看到有三种异常情况发生导致执行的路径不同:
第一种:如果位于0到7字节之间的命令(即try块中的代码)抛出了Class java/lang/Exception类型的异常,则跳转到第10个字节开始执行catch中的代码;
第二种:如果位于0到7字节之间的命令(即try块中的代码)抛出了任何类型的异常,则跳转到第21个字节开始执行finally里面的代码。
第三种:如果位于10到18字节之间的命令(即catch块中的代码)跑出了任何类型的异常,则跳转到第21个字节开始执行finally里面的代码。
第四种:如果位于21到23字节之间的命令(即catch块中的代码)跑出了任何类型的异常,则跳转到第21个字节开始执行finally里面的代码。

LineNumberTable:行号表

为调试器提供源码中的每一行对应的字节码信息。上面的例子中,Java 源码里的第 50 行与 TestTryCatch.java 函数字节码序号 0 相关,第 51 行与字节码序号 4 相关

 LineNumberTable:
        line 50: 0
        line 51: 4
        line 56: 7

LineNumberTable.png

LocalVariableTable:本地变量表

存放局部变量,比如在try中出现异常后,会通过astore_2将Exception存到数组的第3个位置,也就是结合Exception table和LocalVariableTable来完成异常出现时的执行过程;

  LocalVariableTable:
        Start  Length  Slot  Name   Signature
           12      10     2     e   Ljava/lang/Exception;
            0      26     0  this   Lcom/wei/sample/clazz/TestTryCatch;
            0      26     1  args   [Ljava/lang/String;
StackMapTable:栈图

主要用于类加载时校验用的,类加载其中校验这步中会校验字节码是否非法,继承关系,地址偏移是否正确等,不用太关心;
类加载可参考:https://www.jianshu.com/p/071874762b72
StackMapTable具体可参考: https://blog.csdn.net/lijingyao8206/article/details/46715405

StackMapTable: number_of_entries = 2
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/lang/Exception ]
        frame_type = 74 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值