JVM 类文件结构

各种不同平台的虚拟机与所有平台都使用统一的程序存储格式——字节码(ByteCode)——构成平台无关性的基石

Java虚拟机不和包括Java在内的任何一门语言绑定,它只与“Class”文件这种特定的二进制文件格式所关联。

Class类文件结构

  • 任何一个Class文件都对应着唯一一个类或接口的定义信息,但类或接口并不一定以磁盘文件的形式存在,也可以通过类加载器直接生成。

  • Class文件是一组以8位字节为基础单位的二进制流,存储的内容几乎全部是程序运行所必须的数据,没有空隙存在。

  • Class文件格式采用类似于C语言结构体的伪结构存储数据。只有两种数据类型:无符号数

    • 无符号数:属于基本数据类型。u1 u2 u3 u4分别代表1 2 4 8个字节的无符号数。描述数字、索引引用、数量值、字符串值(UTF-8)
    • 表:由多个无符号数或其他表构成的复合数据类型,以“_info”结尾。描述有层次关系的符合结构的数据。

img

魔数

定义:Class文件的前4个字节称为魔数。

作用:确定文件格式。是否是一个可以被虚拟机接受的Class文件。

Class文件魔数:CAFEBABE

版本号

魔数后面的4个字节存储Class文件的版本号。

5 6 字节是次版本号,7 8 字节是主版本号。

Java版本号从 45 开始,对应JDK 1.1。JDK1.6 —— 50 —— 0x0032

常量池(Constant pool)

主次版本号后是常量池入口。

主要存放两类常量:字面量符号引用。字面量类似于java中的常量概念,如文本字符串,final常量等,而符号引用则属于编译原理方面的概念,包括以下三种:

  • 类和接口的全限定名
  • 字段的名称和描述符号
  • 方法的名称和描述符

不同于C/C++, JVM是在加载Class文件的时候才进行的动态链接,也就是说这些字段和方法符号引用只有在运行期转换后才能获得真正的内存入口地址。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建或运行时解析并翻译到具体的内存地址中

//案例:
public class Main {
    
    private int m;
    
    public int inc() {
        return m + 1;
    }
}

//生成的class文件
cafe babe 0000 0034 0013 0a00 0400 0f09
0003 0010 0700 1107 0012 0100 016d 0100
0149 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 0369 6e63
0100 0328 2949 0100 0a53 6f75 7263 6546
696c 6501 0009 4d61 696e 2e6a 6176 610c
0007 0008 0c00 0500 0601 0010 636f 6d2f
7268 7974 686d 372f 4d61 696e 0100 106a
6176 612f 6c61 6e67 2f4f 626a 6563 7400
2100 0300 0400 0000 0100 0200 0500 0600
0000 0200 0100 0700 0800 0100 0900 0000
1d00 0100 0100 0000 052a b700 01b1 0000
0001 000a 0000 0006 0001 0000 0003 0001
000b 000c 0001 0009 0000 001f 0002 0001
0000 0007 2ab4 0002 0460 ac00 0000 0100
0a00 0000 0600 0100 0000 0800 0100 0d00
0000 0200 0e
    
//反编译
//javap -verbose -p Main.class
Classfile /E:/JavaCode/Main.class
  Last modified 2018-4-7; size 362 bytes
  MD5 checksum 4aed8540b098992663b7ba08c65312de
  Compiled from "Main.java"
public class com.rhythm7.Main
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#18         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#19         // com/rhythm7/Main.m:I
   #3 = Class              #20            // com/rhythm7/Main
   #4 = Class              #21            // java/lang/Object
   #5 = Utf8               m
   #6 = Utf8               I
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/rhythm7/Main;
  #14 = Utf8               inc
  #15 = Utf8               ()I
  #16 = Utf8               SourceFile
  #17 = Utf8               Main.java
  #18 = NameAndType        #7:#8          // "<init>":()V
  #19 = NameAndType        #5:#6          // m:I
  #20 = Utf8               com/rhythm7/Main
  #21 = Utf8               java/lang/Object
{
  private int m;
    descriptor: I
    flags: ACC_PRIVATE

  public com.rhythm7.Main();
    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   Lcom/rhythm7/Main;

  public int inc();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field m:I
         4: iconst_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/rhythm7/Main;
}
SourceFile: "Main.java"

可见,反编译后的内容主要是常量池和方法表集合。这里的常量池生声明了实例构造器并声明了一个int类型的字段。方法表集合是对类内部方法的描述。

访问标记(access_flags)

常量池结束后,紧跟着两个字节(u2)的访问标记用于识别类或接口访问信息
在这里插入图片描述

类索引、父类索引与接口索引集合

位置:在access_flags后

类索引(this_class)和父类索引(super_class)都是u2类型的数据,接口索引集合(interfaces)是一组u2类型的数据的集合。

Class文件靠这三项数据确定类的继承关系

  • 类索引用于确定该类的全限定名
  • 父类索引用于确定该类的父类的全限定名
  • 索引集合用于描述该类实现了哪些接口

字段表集合

字段表(field_info)用于描述接口或类中声明的变量。不包括在方法内部声明的变量。

可以表示:字段的作用域(public private protected)、实例变量还是类变量(static)、可变性(final)、并发可见性(volatile)、可否被序列化(transient)、字段数据类型(基本类型、对象、数组)、字段名称

由于各个修饰符都是布尔值,很适合用标志位来表示。而字段名字和字段数据类型都是无法固定的,只能引用常量池中的常量描述。

字段表结构:

类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

方法表集合

方法表用于描述方法信息,方法表结构同字段表一样,依次包括访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)

属性表集合

属性表(attribute_info),在Class文件、字段表、方法表中都可以携带自己的属性表集合,用于描述特殊的属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值