JVM-Class类文件结构笔记

概要:关于学习JVM类文件结构的笔记,主打一手精简但是带一点深度,这里是基于《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》一书进行学习的,但是其中有一些地方并不够清晰或者实例不够充分,这里也会讲述,当然只是拙见,有所不足敬请指出。

前提:类的结构

        类文件的结构是以8位为基础单位的二进制流(原文说的是8字节,就之后的u1 u2都不是8个字节,肯定说不通……)。由两部分组成,无符号数与表。

无符号数:由1字节、2字节、4字节、8字节分别用u1、u2、u3、u8表示, 无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。

:由无符号数与表组成的复合数据结构。通常以_info结尾表示,class文件可以按哲学的方式也看做一张表……

        class类文件是各部分按顺序紧密排列的,顺序如下表,这里附带了解释,以方便一样还在学习的朋友不会看着难受(interface info也与原文不一样,后面会说)

类型

名称

数量

解释

u4

magic

1

魔数

u2

minor_version

1

次版本号

u2

major_version

1

主版本号

u2

constant_pool_count

1

常量池数量

cp_info

constant_pool

constant_pool_count - 1

常量池信息

u2

access_flags

1

访问标志

u2

this_class

1

类索引

u2

super_class

1

父类索引

u2

interface_count

1

实现接口数量

interface_info

interfaces

interface_count

接口索引表

u2

fields_count

1

字段数量

field_info

fields

fields_count

字段信息

u2

methods_count

1

方法数量

method_info

methods

methods_count

方法信息

u2

attributes_count

1

属性数量

attribute_info

attributes

attributes_count

属性信息

1、魔数与版本号

        魔数(magic bumber)用来确认一个文件是否为jvm可以接受的格式,class类文件的是0xCAFEBABE,如下图。版本号紧跟在魔数之后,占用4个字节,前两位是次版本号,后两位是主版本号,java版本号从45(jdk1)开始,如下图,0x33 = 51(D),算下来也就是jdk7(这里用jdk7是为了和书上一致)。

2. 常量池

        根据最前面的表,过了魔数和版本号,就是常量池了。常量池算是最麻烦的一部分了。其有两部分,1是计数,2是内容。计数是一个u2(也就是两个字节),用来表示有多少项常量,然后是内容,对应一个一个的常量项。这里提供一个实例:

public class ModelFinal implements InterfaceB, InterfaceC {
    private final static Long MODEL_LONG = 1L;
    private final int modelInt = 99;
    private String modelMsg;

    public ModelFinal() {
    }

    public ModelFinal(String modelMsg) {
        this.modelMsg = modelMsg;
    }

    public String modelInfo() {
        return modelMsg + modelInt + MODEL_LONG;
    }
}

        其父接口都是空的,主要为了验证后面的interface集合。用jdk7编译后,使用winhex打开class文件,用数据显示器可以看到常量计数总数是60,但是使用的常量的计数索引是从1开始的,意思就是只有59个常量项,还有一个索引0是留着给没有引用常量的地方使用的(59+1=60)。

        知道了常量池的构成后,就讲详细的常量项是怎么样的,任何一个常量项,它的第一个u1,都是表明它的类型的,比如上面那张图,接着的一个常量项就是0A,可以查表找到它的意义,如下

 然后再去查表知道它的具体结构,如下图,具体的表太多了,这里贴篇博客参考:JVM——类文件结构_u4acc-CSDN博客

 如果只看字节码,哪我们要对照实在有点麻烦,每次都需要去查表,于是可以使用java自带的javap -verbose class类文件的命令,查看类的常量池,前面的代码反汇编后如下图(部分):

3. 访问标志 

        访问标志是用来描述类的,像public,final,static等等(不需要多讲吧?),是一个u2。在全是16进制中的文件里面怎么找到什么地方开始才是访问标志呢?以winhex为例,看下图。因为class的各部分是紧密存放的,我们的代码中可没有感叹号,然后看其是一个u2,而且后面都没有asscii码能表示的字符了,于是猜测这个是访问标志开始了。

        光猜不行,我们得算一算。比如前面代码中的类

        类标志是public,查下面这张访问标志表可知,acc_public和acc_super为真,直接加起来,

0x1+0x20=0x21,和上面的对的上。至于为什么要把他们加起来,这个本质上其实是用二进制存储的信息,不同位代表不同的状态,加起来说明有某两种状态。比如0x0001,0x0010,只以后两个16进制位举例,是00000001与00010000。也就是说,第1位置1表示public,第5位置1表示final,两位同时置1表示两者都有,都置1和把他们加起来也就一样了,计算机专业应该都能看懂。

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

        紧接着访问标志的的就是类和父类索引这些了,主要是描述这个类的继承关系。我们都知道java只能有一个父类(而且默认都是Object),所以类索引和父类索引肯定都只有一个,这二者都用u2表示。在winhex里面查访问标志后面的两位,可知类索引是0x0c也就是十进制12,父类索引是0x0d,也就是十进制13

        查反汇编的表,如下图,索引项能对上。需要说的是,后面讲的某某索引,都是指其在常量池中的索引。

        然后就是接口索引集合了,首先其一个u2表示有多少个集合,上面代码的例子中有两个接口,如下图

        从字节码来看,21是标志位,然后4个字节的类与父类索引,然后就是接口集合了,这里确实是2,也就是两个。紧跟其后就是其索引,为0x0E和0x0F,也就是14与15。

反汇编查常量表,发现确实能对上。

        此处需要勘误《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》,书上是这样写的,接口计数用的是u2,然后接口的索引列表还是用的u2,这明显是不对的,应该是一个表,表的每一项都是u2大小的索引,指向常量表中的项。

5. 字段表集合 

首先把字节码贴上,方便后面参照

0x03开始是字段表集合,这里表示类一共有3个字段。作为验证,确实如此,见下图

字段的表结构如下

于是可知上面的0x03计数值后面跟着的前6个字节是一个字段的访问信息,简单名索引,描述符索引。然后是属性集合。

这里先看访问标志,0x001A。访问标志如下表

按private+final+static的结果是0x0002+0x0010+0x0008=0x001A,所以这是代码中第一个变量的访问标志。

然后是简单名称索引,这里是0x10,也就是常量池索引16的项(见下方),可见确实是对应的变量名。

然后是描述符索引,0x11,也就是常量池索引17:

也对的上。

然后是属性表集合,用于存放额外信息,这里因为是Long对象,所以没有,前面代码中的第二个变量是int,且有赋值,所以它是有这项属性的,快速对照:

第一个是权限描述符,private+final = 0x10+0x02=0x12

第二个是简单名称索引,0x12=18d,

第三个是描述符索引,0x13=19d,

,也就是int

第四个是属性集合大小,这里是1,所以有一项属性

第五个就是唯一的一项属性所以,0x14=20d,查索引20的常量

一切都对得上,已经完美了……属性表集合后面的属性表集合详细讲解

*全限定名与简单名:上面提到的类索引指向的就是全限定名。简单名就是字段和方法没有任何类型和参数等修饰的名字,如上面的MODEL_LONG。

*描述符: 用来描述字段 的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本类型和void都用一个大写字母来表示, 对象类型则用字符L加对象的全限定名来表示。

于是这里就可以知道上面的Ljava/lang/Long的意思了说明这是一个对象,权限的名是java/lang/Long。对于数组来说,会在其前面加一个[来描述,比如int[]就是[I,long[][]就是[[J。

方法的描述符放在方法集合里面讲。

===============================================

下班了,后面的以后再补上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值