Java虚拟机-Class文件结构之方法表

2019-07-31  本文已影响0人  贪睡的企鹅

1 解析示例

示例源码

public class ClassStructureMethod {
    public  void greeting() throws Exception {
       try {
           int a=1;
           int b=1;
           int c=a+b;
           System.out.println(c);
       }catch (Exception e){
           System.out.println("catch");
       }finally {
           System.out.println("finally");
       }
    }
}

示例常量池

Constant pool:
   #1 = Methodref          #9.#30         // java/lang/Object."<init>":()V
   #2 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #33.#34        // java/io/PrintStream.println:(I)V
   #4 = String             #35            // finally
   #5 = Methodref          #33.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #37            // java/lang/Exception
   #7 = String             #38            // catch
   #8 = Class              #39            // jvm/ClassStructureMethod
   #9 = Class              #40            // java/lang/Object
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Ljvm/ClassStructureMethod;
  #17 = Utf8               greeting
  #18 = Utf8               a
  #19 = Utf8               I
  #20 = Utf8               b
  #21 = Utf8               c
  #22 = Utf8               e
  #23 = Utf8               Ljava/lang/Exception;
  #24 = Utf8               StackMapTable
  #25 = Class              #37            // java/lang/Exception
  #26 = Class              #41            // java/lang/Throwable
  #27 = Utf8               Exceptions
  #28 = Utf8               SourceFile
  #29 = Utf8               ClassStructureMethod.java
  #30 = NameAndType        #10:#11        // "<init>":()V
  #31 = Class              #42            // java/lang/System
  #32 = NameAndType        #43:#44        // out:Ljava/io/PrintStream;
  #33 = Class              #45            // java/io/PrintStream
  #34 = NameAndType        #46:#47        // println:(I)V
  #35 = Utf8               finally
  #36 = NameAndType        #46:#48        // println:(Ljava/lang/String;)V
  #37 = Utf8               java/lang/Exception
  #38 = Utf8               catch
  #39 = Utf8               jvm/ClassStructureMethod
  #40 = Utf8               java/lang/Object
  #41 = Utf8               java/lang/Throwable
  #42 = Utf8               java/lang/System
  #43 = Utf8               out
  #44 = Utf8               Ljava/io/PrintStream;
  #45 = Utf8               java/io/PrintStream
  #46 = Utf8               println
  #47 = Utf8               (I)V
  #48 = Utf8               (Ljava/lang/String;)V

2 方法表前置结构

image

2 方法表结构

方法表集合的结构

方法表集合的结构被划分为方法表数量方法表集合。

方法表数量(methods_count):表示类中定义的方法个数,其中除了自定义的方法外,每一个类中都会存在名称为"<init>"构造函数,如果类中定义静态变量或者静态块,则还会存在一个名称为"<clinit>"类初始化方法。

方法表(method_info):表示类中定义某一个方法。其中涵盖了方法的所有信息。

image

3 方法表数量(methods_count)

Java虚拟机规范中方法表数量用2个字节(u2)用来表示。

示例中方法表数量

示例中方法数量,对应16进制0x0002,即十进制的2,代表Class文件中有2个方法。

image

对比源代码中ClassStructureMethod类定义,存在一个构造方法<init>和自定义方法greeting(),共两个方法。

public class ClassStructureMethod {
    public  void greeting() throws Exception {
       try {
           int a=1;
           int b=1;
           int c=a+b;
           System.out.println(c);
       }catch (Exception e){
           System.out.println("catch");
       }finally {
           System.out.println("finally");
       }
    }
}

Class文件中init方法的描述

本章节主要用来解析greeting方法在Class文件中的结构,在此跳过<init>方法部分描述。

Class文件中init方法的描述

image

4 方法表(methods_info)

Java虚拟机规范定义方法表结构

image
4.1 方法访问标志(access_flags)

Java虚拟机规范中方法访问标志用2个字节(u2)用来表示。

Java虚拟机规范定义方法访问标志

image

示例中方法访问标志

字段修饰符用2个字节(u2)来描述,对应16进制0x0001,对照JVM规范表中方法方法访问标志为ACC_PUBLIC,表示方法被"public"修饰

image

对比源代码中可以发现greeting方法的修饰符为"public"相匹配

public class ClassStructureMethod {
    public  void greeting() throws Exception {
       try {
           int a=1;
           int b=1;
           int c=a+b;
           System.out.println(c);
       }catch (Exception e){
           System.out.println("catch");
       }finally {
           System.out.println("finally");
       }
    }
}
4.2 方法名称(name_index)

Java虚拟机规范中方法名称用2个字节(u2)用来表示。可以换算成为一个整数,这个整数是常量池数组的一个下标,指向中一个CONSTANT_Utf8_info类型的常量用来描述方法的名称。

示例方法名称

方法名用2位字节(u2)来描述,对应16进制0x0011,即十进制的17,17是常量池数组的一个下标,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Utf8_info类型的常量(下标#17),对应字符串"greeting",表示方法名称

image
4.3 方法描述符(descriptor_index)

按照Java虚拟机规范方法描述符用2位字节(u2)来描述,可以换算成为一个整数,这个整数是常量池数组的一个下标,指向中一个CONSTANT_Utf8_info类型的常量用来描述方法描述符。

关于方法的描述符在常量池中讲过主要用来描述方法的参数和返回类型。

示例中方法描述符

方法修饰符用2个字节(u2)描述,对应16进制0x000B,即十进制的11,11是常量池数组的一个下标,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Utf8_info类型的常量(下标#11),对应字符串"()V",表示greeting方法不需要传递参数,且不需要返回值

image

4.4 方法中的属性

4.4.1 属性表

属性表(attribute_info)作用于Class文件、字段表、方法表。它们都可以携带自己的属性集合,用于描述某些场景专有的信息。

4.4.2 虚拟机规范中定义属性表类型
image
4.4.3 属性表集合的结构

属性表集合的结构被划分为属性表数量属性表集合。

属性表数量(methods_count):表示方法中定义的属性个数。

属性表(method_info):表示方法中定义某一个属性。其中涵盖了属性的所有信息。

4.4.4 用于方法中属性表

对于方法来说常用到的属性有如下几种:"code","Exception","Deprecated","Synthetic"。

4.4.5 属性表通用格式

属性名称索引:属性名称索引占用2个字节,可以换算成为一个整数,这个整数是常量池数组的一个下标,指向中一个CONSTANT_Utf8_info类型的常量用来描述属性名称。通过属性名称就可以确定属性表类型

属性长度:占用4个字节,它的值表示紧跟其后的多少个字节是拿来表示这个属性信息的。

属性值:由若个个字节构造,字节的大小取决属性的长度

image

4.5 属性表数量

Java虚拟机规范中属性表数量用2个字节(u2)用来表示。

示例中属性表数量

示例中属性表,对应16进制0x0002,即十进制的2,代表方法中有2个属性。

image

4.6 属性表

4.6.1 Code属性
4.6.1.1 Code属性名称索引

属性名称索引用2个字节(u2)描述,对应16进制0x000C,即十进制的12,12是常量池数组的一个下标,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Utf8_info类型的常量(下标#12),对应字符串"code",表示当前属性code类型的属性。

image
4.6.1.2 Code属性结构

通过属性名称索引知道了方法的第一个索引名称为"code".其虚拟机规范中的定义如下:

image
4.6.1.3 Code属性长度

属性长度索引用4个字节(u4)描述,对应16进制0x000000EB,即十进制的235,表示属性的长度占用235个字节

image
4.6.1.4 Code属性max_stack

操作数栈深度的最大值用2个字节(u2)描述,对应16进制0x002,即十进制的2,表示方法中操作数栈深度最大值为2

image
4.6.1.5 Code属性max_locals

最大局部变量数目用2个字节(u2)描述,对应16进制0x005,即十进制的5,表示最大局部变量数目为5

image
4.6.1.6 Code属性code_length

java字节码的长度用4个字节(u4)描述,对应16进制0x0000003C,即十进制的60,表示ava字节码的长度为60

image
4.6.1.7 Code属性code

java字节码的长度用60个字节描述

image
4.6.1.8 Code属性中异常表集合

异常表集合的结构被划分为异常表数量异常表集合。

异常表数量(exceptions_count):表示方法代码中可能存在抛出异常个数。

异常表(exception_info):表示代码中一个异常。

4.6.1.8.1 Code属性中异常表数量

Code属性中异常表数量用2个字节(u2)描述,对应16进制0x004,即十进制的4,表示异常表数量为4

image
4.6.1.8.2 Code属性中异常表结构

异常表用来表示的意思是:如果字节码从第start_pc行到第end_pc行之间出现了catch_type所描述的异常类型,那么将跳转到handler_pc行继续处理

名称 字节长度 含义
start_pc 2个字节 异常发生起始字节指令行
end_pc 2个字节 异常发生结束字节指令行
handler_pc 2个字节 异常处理字节指令行
catch_type 2个字节 异常类型
image

这里同样可以使用javap反编译工具来解析获取。我们在此比对下,需要注意的是这里行表示的是反编译工具中字节指令的行而非源代码中的行

       stack=2, locals=5, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_1
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: istore_3
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_3
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: ldc           #4                  // String finally
        20: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        23: goto          59
        26: astore_1
        27: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        30: ldc           #7                  // String catch
        32: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        35: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        38: ldc           #4                  // String finally
        40: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        43: goto          59
        46: astore        4
        48: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        51: ldc           #4                  // String finally
        53: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        56: aload         4
        58: athrow
        59: return
      Exception table:
         from    to  target type
             0    15    26   Class java/lang/Exception
             0    15    46   any
            26    35    46   any
            46    48    46   any

对比源代码

public class ClassStructureMethod {
    public  void greeting() throws Exception {
       try {
           int a=1;
           int b=1;
           int c=a+b;
           System.out.println(c);
       }catch (Exception e){
           System.out.println("catch");
       }finally {
           System.out.println("finally");
       }
    }
}

code属性其他异常如下

image
4.6.1.9 Code属性中的属性

code属性内部同样有属性集合

属性表集合的结构同样被划分为属性表数量和属性表集合。

属性表数量(methods_count):表示方法中定义的属性个数。

属性表(method_info):表示方法中定义某一个属性。其中涵盖了属性的所有信息。

4.6.1.9.1 Code属性中属性类型

Code属性中定义的属性主要有“LineNumberTable”类型和“LocalVariableTable”类型

4.6.1.9.2 Java虚拟机定义的LineNumberTable
image

line_number_table结构

名称 字节长度 含义
start_pc 2个字节 字节码行号
line_number 2个字节 java 源码行号
4.6.1.9.3 Java虚拟机定义的LocalVariableTable

[图片上传失败...(image-2d7657-1564563120952)]%20%E5%B1%9E%E6%80%A7.png)

local_variable_table结构

image

如果不太理解可以看下图

image
4.6.1.9.4 Javap反编译结果
 LineNumberTable:
        line 6: 0
        line 7: 2
        line 8: 4
        line 9: 8
        line 13: 15
        line 14: 23
        line 10: 26
        line 11: 27
        line 13: 35
        line 14: 43
        line 13: 46
        line 15: 59
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            2      13     1     a   I
            4      11     2     b   I
            8       7     3     c   I
           27       8     1     e   Ljava/lang/Exception;
            0      60     0  this   Ljvm/ClassStructureMethod;
4.6.2 Exceptions 属性

Exception属性是在方法表中与Code属性平级的一项属性,切勿与Code属性中的异常混淆。Exception属性的作用是列举出方法中可能抛出的受查异常(Checked Exception),也就是方法描述时在 throws 关键字后面列举的异常。

Java虚拟机定义的Exceptions

image

示例中Exceptions属性

image

4 总结

可以用一张图来描述上诉方法的结构

image

javap完整结果

Classfile /C:/work/project/juc-in-action/target/classes/jvm/ClassStructureMethod.class
  Last modified 2019-7-30; size 861 bytes
  MD5 checksum c291986bf1b6bc3997d2e13aff91e614
  Compiled from "ClassStructureMethod.java"
public class jvm.ClassStructureMethod
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #9.#30         // java/lang/Object."<init>":()V
   #2 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #33.#34        // java/io/PrintStream.println:(I)V
   #4 = String             #35            // finally
   #5 = Methodref          #33.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #37            // java/lang/Exception
   #7 = String             #38            // catch
   #8 = Class              #39            // jvm/ClassStructureMethod
   #9 = Class              #40            // java/lang/Object
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               Ljvm/ClassStructureMethod;
  #17 = Utf8               greeting
  #18 = Utf8               a
  #19 = Utf8               I
  #20 = Utf8               b
  #21 = Utf8               c
  #22 = Utf8               e
  #23 = Utf8               Ljava/lang/Exception;
  #24 = Utf8               StackMapTable
  #25 = Class              #37            // java/lang/Exception
  #26 = Class              #41            // java/lang/Throwable
  #27 = Utf8               Exceptions
  #28 = Utf8               SourceFile
  #29 = Utf8               ClassStructureMethod.java
  #30 = NameAndType        #10:#11        // "<init>":()V
  #31 = Class              #42            // java/lang/System
  #32 = NameAndType        #43:#44        // out:Ljava/io/PrintStream;
  #33 = Class              #45            // java/io/PrintStream
  #34 = NameAndType        #46:#47        // println:(I)V
  #35 = Utf8               finally
  #36 = NameAndType        #46:#48        // println:(Ljava/lang/String;)V
  #37 = Utf8               java/lang/Exception
  #38 = Utf8               catch
  #39 = Utf8               jvm/ClassStructureMethod
  #40 = Utf8               java/lang/Object
  #41 = Utf8               java/lang/Throwable
  #42 = Utf8               java/lang/System
  #43 = Utf8               out
  #44 = Utf8               Ljava/io/PrintStream;
  #45 = Utf8               java/io/PrintStream
  #46 = Utf8               println
  #47 = Utf8               (I)V
  #48 = Utf8               (Ljava/lang/String;)V
{
  public jvm.ClassStructureMethod();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ljvm/ClassStructureMethod;

  public void greeting() throws java.lang.Exception;
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=5, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_1
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: istore_3
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_3
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: ldc           #4                  // String finally
        20: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        23: goto          59
        26: astore_1
        27: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        30: ldc           #7                  // String catch
        32: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        35: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        38: ldc           #4                  // String finally
        40: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        43: goto          59
        46: astore        4
        48: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        51: ldc           #4                  // String finally
        53: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        56: aload         4
        58: athrow
        59: return
      Exception table:
         from    to  target type
             0    15    26   Class java/lang/Exception
             0    15    46   any
            26    35    46   any
            46    48    46   any
      LineNumberTable:
        line 6: 0
        line 7: 2
        line 8: 4
        line 9: 8
        line 13: 15
        line 14: 23
        line 10: 26
        line 11: 27
        line 13: 35
        line 14: 43
        line 13: 46
        line 15: 59
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            2      13     1     a   I
            4      11     2     b   I
            8       7     3     c   I
           27       8     1     e   Ljava/lang/Exception;
            0      60     0  this   Ljvm/ClassStructureMethod;
      StackMapTable: number_of_entries = 3
        frame_type = 90 /* same_locals_1_stack_item */
          stack = [ class java/lang/Exception ]
        frame_type = 83 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 12 /* same */
    Exceptions:
      throws java.lang.Exception
}
SourceFile: "ClassStructureMethod.java"

上一篇下一篇

猜你喜欢

热点阅读