1 类文件数据结构类型

Class文件结构主要有两种数据结构:无符号数和表

无符号数:用来表述数字,索引引用、数量值以及字符串等,比如 图1中类型为u1,u2,u4,u8分别代表1个字节,2个字节,4个字节,8个字节的无符号数

:表是有由多个无符号数以及其它的表组成的复合结构,比如图1中类型以_info结尾的项为表类型。

2 类结构定义

Class类文件是紧凑、顺序、无空隙的,魔数(MagicNumber)、Class文件版本(Version)、常量池(Constant_Pool)、访问标记(Access_flag)、本类(This_class)、父类(Super_class)、接口(Interfaces)、字段集合(Fields)、方法集合(Methods )、属性集合(Attributes)。其中因为java多继承所以interfaces接口类型为数组;attribute_info则是方法表中定义的code索引,指向具体的方法体字节码。如图1所示。

下面用一段程序做说明,此类有接口,有方法、类变量和实例变量,机器是如何识别字节码然后按照上面的规则来定义此class类呢?

package com.jd.crm.Logback;

public class TestClass implements Super{

    private static final int staticVar = 0;

    private int instanceVar=0;

    public int instanceMethod(int param) throws  Exception{
        return param ++;
    }
}
interface Super{ }

通过javap帮助解析class文件格式如下:

Classfile /D:/spm-workspace/test/target/classes/com/jd/crm/Logback/TestClass.class
  Last modified 2023-4-14; size 597 bytes
  MD5 checksum 9d5dd9fc2145ac17393fee7a707d3b9c
  Compiled from "TestClass.java"
public class com.jd.crm.Logback.TestClass implements com.jd.crm.Logback.Super
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#26         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#27         // com/jd/crm/Logback/TestClass.instanceVar:I
   #3 = Class              #28            // com/jd/crm/Logback/TestClass
   #4 = Class              #29            // java/lang/Object
   #5 = Class              #30            // com/jd/crm/Logback/Super
   #6 = Utf8               staticVar
   #7 = Utf8               I
   #8 = Utf8               ConstantValue
   #9 = Integer            0
  #10 = Utf8               instanceVar
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lcom/jd/crm/Logback/TestClass;
  #18 = Utf8               instanceMethod
  #19 = Utf8               (I)I
  #20 = Utf8               param
  #21 = Utf8               Exceptions
  #22 = Class              #31            // java/lang/Exception
  #23 = Utf8               MethodParameters
  #24 = Utf8               SourceFile
  #25 = Utf8               TestClass.java
  #26 = NameAndType        #11:#12        // "<init>":()V
  #27 = NameAndType        #10:#7         // instanceVar:I
  #28 = Utf8               com/jd/crm/Logback/TestClass
  #29 = Utf8               java/lang/Object
  #30 = Utf8               com/jd/crm/Logback/Super
  #31 = Utf8               java/lang/Exception
{
  public com.jd.crm.Logback.TestClass();
    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_0
         6: putfield      #2                  // Field instanceVar:I
         9: return
      LineNumberTable:
        line 3: 0
        line 7: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/jd/crm/Logback/TestClass;

  public int instanceMethod(int) throws java.lang.Exception;
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iload_1
         1: iinc          1, 1
         4: ireturn
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/jd/crm/Logback/TestClass;
            0       5     1 param   I
    Exceptions:
      throws java.lang.Exception
    MethodParameters:
      Name                           Flags
      param
}
SourceFile: "TestClass.java"

以上是javap帮助我们生成的class文件解析结果,只是给人看,而非机器。

通过编译后生成class文件格式如下,因为class文件是以8位作为一个字节的二进制流。为了方便计算,用16进制表示二进制(1个字节=2个十六进制的数,故下面每2个数就代表1个字节)

2.1 魔法数

前四个字节cafebabe是固定值,任何语言编译成jvm认识的二进制流,前四位必须是固定的cafebabe字节。

2.2 版本号

紧接着2个字节00表示次版本号为0 ;0034代表主版本为52(jdk版本号对应的jdk版本为1.8)参考jdk版本和class字节版本的对应关系

2.3 常量个数

常量个数const_pool_count字节码为00 20对应的说明常量个数为32,实际为31个,因为首位jvm作为保留位使用。

2.4 常量池

常量池存放两大常量:字面量和符号引,字面量如文本字符串,被生命的final常量值等,而符号引用则包含类、接口的全限名称、字段、方法名称和描述符号等等。参考javap生成的类文件信息。

这里只分析下其中一个常量,在上面常量个数2个字节后面紧接着一个字节0a十进制为10,参考常量池类型10代表类中方法的符号引用。继续参考方法类型MethodRef_info个格式定义:前两个字节0004代表方法所在类名称的索引,后两个字节0001a代表一个NameAndType类型的索引。

2.5 类访问标志

紧接常量池定义完后的u2标识访问标志,本例标识为0x0021和下图标志位按位或计算,如0x0001为真,0x0020也为真,其他为否 最终确认访问标志位ACC_PUBLIC、ACC_SUPER

2.6 本类、父类、接口索引集合

根据图1的规则,u2两个字节0003标识当前类名的引用到,引用常量池数组下标为#3,根据图3所示子项的类名为com/jd/crm/Logback/TestClass;0004代表父类类名的引用常量池数组下标为#4,根据图4所示引用的父类类名为java/lang/Object;紧接着0001标识接口个数,指明数量为1,0005标识第一个接口数组中接口的名称,指向常量池中下标为5的名称为com/jd/crm/Logback/Super;

比如查找当前类索引如下图

2.7 字段表集合

字段表以数组的形式定义存储在常量表中

以上图说明,0002标识域个数为2个域标识,在本类中有两个,一个类的域字段staticVar 一个是实例对象的域字段instanceVar,如字段结构定义(下图)定义,前2个字节001a为访问标识,和类访问标识一样,分别用001a的二进制和下图字段域访问标识类型做位或运算,得出访问类型为ACC_PRIVATE类型。name_index的占用两个字节0006,指向常量表下标为6的引用,descriptor_index=0007指向常量表下标为7的引用,此处为I标识为数据类型为int,attributes_count=0001为1个,值为0008指向常量表下标为#8的引用常量ConstantValue,标识为静态变量,最终依次类推第二个域标识引用

字段结构定义

字段域的访问标志请参考类访问标志,逻辑计算一致,只是规则不一样而已 如下图

2.8 方法表集合

和域字段集合表定义类似 也是数组方式定义在常量池中 ,其中方法的结构体第四个字段attributes_count代表方法的属性数量,attribute_info就是属性的集合参考属性表集合

方法表访问标识类型

通过上面方法的访问标志、名称索引和描述索引定义方法的基本信息,方法的代码块则存放于类型为Code的属性表中。

2.9 属性表集合

类、字段表、方法表本身可包含属性表,属性表格结构体如下,属性表结构类型较多,比如有Code类型、Exception类型、MethodParameters类型等等,具体参考属性表类型。所有的属性都是引用常量池中的属性类型名称。然后根据属性的长度指定该属性的内容,根据属性的不同类型解析不同的属性值。格式定义如下

以Code属性举例,Code属性结构如下所示

jvm按属性获取attribute_name_index指向常量池一个字符串常量Code,紧接着attribute_length标识Code类型Info信息长度,这个info内容包括:max_stack 最大栈深,max_locals局部变量槽数量,code_length标识机器字节码长度,往后查询字节码如下图所示,其实就是0/1/4/5/6/9的指令集。Code类型又嵌套异常属性表、行号表LineNumberTable、LocaVariableTable 局部变量表等等信息。如下图javap生成的类定义信息

1.Code1方法执行过程:

构造方法:descriptor ()V标识无参无返回值为Void的方法索引,flags可见性修饰符;

程序运行时,先将常量池、方法字节码、字符串常量池,静态变量加载到元数据区(1.8后字符串常量池,静态变量放入了堆);main线程开始运行,分配栈帧内存,其中操作数栈stack=2表示运行该方法所需要的最大操作数栈的深度是2;locals=1表示该运行方法所需要的最大局部方法表的最大slot数据是1;args_size是该方法的形参个数,如果是实例方法 第一个形参是this引用。此例正是this引用。所以args_size=1+实际的参数

aload_0: 加载 slot0的局部变量,即this,作为下面的invokespecial 构造方法调用的参数

invokespecial: 调用构造方法,常量池第#1项,即【Method java/lang/Object.""

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/jingdongkeji/p/17463760.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!

相关课程