zoukankan      html  css  js  c++  java
  • Java字节码方法表与属性表深度剖析

    方法表:

    在上一次咱们已经分析到了字段信息了,如下:

    紧接着就是方法相关的信息了:

    而它展开之后的结构为:

    所以往后数2个字节,看一下方法的总数:

    3个方法,可咱们只定义了两个方法呀:

    因为编译器会为我们生成一个默认的构造方法,所以就3个了,那每个方法的具体信息是啥呢?它是一个method_info类型的,如下:

    也就是方法表,当然也有它自己的一个结构,下面来看一下:

    • access_flags:占用两个字节,表示访问标记。
    • name_index:占用两个字节,名字索引,指向的是常量池。
    • descriptor_index:占用两个字节,描述索引,指赂的是常量池。
    • attributes_count:占用两个字节,属性个数,如果为0,则下面的属性表就不显示了。
    • attributes::属性表。

    用结构形式来表示:

    那按照上面的表先来看第一个方法的访问标记,往后读两个字节:

    查看下访问修饰符表,对应于:

    表示是一个public的方法,接下来两个字节则表示方法名字索引,走着:

    对应常量池:

    再往下二个字节则表示描述符索引:

    对应常量池:

    说明该方法是一个默认构造方法。从javap -verbose中也能对应上:

    属性表:

    接下来二个字节为属性个数:

    表示有一个属性,所以属性表中的个数也为1,而属性表是attribute_info类型,很显然也有它自己的结构,那长啥样呢?

    • attribute_name_index:占2个字节,表示属性名字的索引,指向常量池。
    • attribute_length:占4个字节,表示属性的长度。
    • info[attribute_length]:占1个字节,表示具体的信息。

    依照上面的顺序,先数2个字节:

    对应常量池:

    其实在javap -verbose中也能看到每个方法都有一个Code字样,如下:

    Constant pool:
       #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V
       #2 = Fieldref           #3.#21         // com/jvm/bytecode/MyTest1.a:I
       #3 = Class              #22            // com/jvm/bytecode/MyTest1
       #4 = Class              #23            // java/lang/Object
       #5 = Utf8               a
       #6 = Utf8               I
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcom/jvm/bytecode/MyTest1;
      #14 = Utf8               getA
      #15 = Utf8               ()I
      #16 = Utf8               setA
      #17 = Utf8               (I)V
      #18 = Utf8               SourceFile
      #19 = Utf8               MyTest1.java
      #20 = NameAndType        #7:#8          // "<init>":()V
      #21 = NameAndType        #5:#6          // a:I
      #22 = Utf8               com/jvm/bytecode/MyTest1
      #23 = Utf8               java/lang/Object
    {
      public com.jvm.bytecode.MyTest1();
        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: iconst_1
             6: putfield      #2                  // Field a:I
             9: return
          LineNumberTable:
            line 3: 0
            line 4: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      10     0  this   Lcom/jvm/bytecode/MyTest1;
    
      public int getA();
        descriptor: ()I
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #2                  // Field a:I
             4: ireturn
          LineNumberTable:
            line 7: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/jvm/bytecode/MyTest1;
    
      public void setA(int);
        descriptor: (I)V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: iload_1
             2: putfield      #2                  // Field a:I
             5: return
          LineNumberTable:
            line 11: 0
            line 12: 5
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       6     0  this   Lcom/jvm/bytecode/MyTest1;
                0       6     1     a   I
    }
    SourceFile: "MyTest1.java"

    那它表示啥意思呢?其实是表示方法执行的代码,指的是:

    当然啦在字节码文件中不可能是跟源文件中看到的一样,而是通过了一些助记符进行了处理,如下:

    这个在未来进行详细学习的,好,继续来分析属性,接下来4个字节表示属性的长度,如下:

    说明属性的长度为56,然后最后一个字节表示info信息,也就是code的具体信息,这块是比较复杂的,下面先来了解一些理论:

    • JVM预定义了部分attribute,但是编译器自己也可以实现自己的attribute写入class文件里,供运行时使用。
    • 不同的attribute通过attribute_name_index来区分。

    其中JVM预定义的attribute为如下表:

    Code结构:

    这部分东东是比较多的,这次只先对其结构有个初步了解既可,它的作用是保存该方法的结构,如所对应的字节码:

    • attribute_length表示attribute所包含的字节数,不包含attribute_name_index和attribute_length字段。
    • max_stack表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。
    • max_locals表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
    • code_length表法该方法所包含的字节码的字节数以及具体的指令码。
    • 具体的字节码既是该方法被调用时,虚拟机所执行的字节码。
    • exception_table:这里存放的是处理异常的信息。
    • 第一个exception_table表项由start_pc、end_pc、handler_pc、catch_type组成。
    • start_pc和end_pc表示在code数组中的从start_pc到end_pc处(包含start_pc,不包含end_pc)的指令抛出的异常会由这个表项来处理。
    • handler_pc表示处理异常的代码的开始处。catch_type表示会被处理的异常类型,它指向常量池里的一个异常类。当catch_type为0时,表示处理所有的异常。

    这么多陌生的字段,直接晕掉,木要着急,先有个大概了解,在未来学习中会吃透它的,好,先来回到字节码中继续分析,其中code中属性的长度为56:

    接下来2个字节表示max_stack:

    再接下来2个字节表示max_locals:

    对应javap -verbose:

    接下来4个字节表示code的长度:

    code_length=10,而此时发现在javap -verbose中貌似木有找到对应的:

    那接下来的分析没有了参照就不知道我们自己分析的对不对了,对于学习效果会大打折扣了,此时就得借助于另外一个工具来参照了,该工具为jclasslib,gitbub地址:https://github.com/ingokegel/jclasslib,它显示的信息就会比javap -verbose要详细很多,访问一下官网:

    它包含独立的软件和IntelliJ IDEA插件化的方式,所以都装一下,先下载mac安装包:

    具体安装就不概述了,装好之后用它来打开我们的字节码文件既可,长这样:

    同时可以给IDE装上插件,更加便于分析,如下:

    安装好之后,直接就可以在当前打开的java文件中执行这个菜单选项既可:

    看到的效果跟独立的软件看到的是一样的,好,下面来用这个新工具来瞅一眼看到的信息:

    对比下javap -verbose:

    差不多,不过jclasslib工具可以看到JDK的版本,接下来就是常量池:

    但实际是只有23个,展开看一下:

    对于javap -versbose:

    明显要丰富许多,继续往下看:

    其中是可以直接点击链到对应的常量池的,如下:

    接着就是常量池的信息了:

    展开之后,索引都能链接上去,非常之方便:

    接着就是接口信息,目前木有接口:

    然后就到了字段信息了,目前只有一个字段:

    然后再是方法信息,有三个方法:

    点击其中一个看一下:

    有code信息:

    跟javap -versbose是对应上的:

    最后是附加信息:

    LineNumberTable:这个属性用来表示code数组中的字节码和Java代码行数之间的关系。这个属性可以用来在调试的时候定位代码执行的行数。比如说程序抛异常了,而程序执行的是字节码文件,怎么我们就能看到具体报错在源码中的行数呢,其实就是通过该信息做到的。

    而它的结构体为:

    跟javap -verbose中是能对应上的:

    最后则是类的属性了:

    可见jclasslib的结构跟咱们理论上看到的是一模一样的,所以有了它也能让我们在未来学习code这块的结构更加清晰,这是javap -verbose不能达到的。

  • 相关阅读:
    markdown keynote
    pyecharts
    运行成功
    python发邮件3
    python发邮件2
    python发邮件1
    python发邮件
    python中的编码声明
    auther tonyxiao
    111
  • 原文地址:https://www.cnblogs.com/webor2006/p/9459681.html
Copyright © 2011-2022 走看看