classfile解析的内容

1. class文件结构

首先我们先编写一段代码

package com.kevin;

public class Testjvm {
    public static void main(String[] args) {
        System.out.println("Hello Word");
    }
}

然后运行程序生成 .class 文件

package com.kevin;

public class Testjvm {
    public Testjvm() {
    }

    public static void main(String[] args) {
        System.out.println("Hello Word");
    }
}

使用Idea插件打开 class文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class文件是一组以8个字节为基础单位的二进制字节流。

各项数据会严格的按照顺序紧凑的排列在class文件中

中间没有分隔符,使得class文件存储的内容几乎全部都是程序运行的。

2. Classfile 文件结构解析

Java虚拟机规范规定的,class文件格式采用的类似C语言的结构体的伪结构来存储的,这种结构只有两种数据类型。 无符号数 和 表
无符号数:

  • 属于基本数据类型 主要用于描述数字 索引符号 数量值 或者按照UTF-8编码构成的字符串值 数据类型 u1 u2 u4 u8
    也只是逻辑上的区分。

    • u1 —表示一个字节
    • u2 —表示两个字节
    • U4 — 依次类推
    • u8 —依次类推

表:

  • 由多个无符号数或者其他表作为数据项构成的复合数据类型。所有的表都习惯以_info结尾 表主要用于描述有层次关系的复合结构数据。 比如
    方法、字段 需要注意的是class文件没有分隔符,所以每个二进制数据类型都是严格定义的 具体的顺序如下:

在这里插入图片描述

2.1 魔数

  1. 每一个class文件的头4个字节 被称为魔数 magicNumber
  2. 唯一做用是用于确定这个文件是否为一个能被虚拟机接受的class文件
  3. cass文件魔数值为0xCAFEBABE 如果以个文件不是以CAFEBABE开头,那么它就肯定不是java的class文件。

那么它也是java.class的识别魔数。

2.2 class文件版本号

紧挨着魔数的4个字节表示class的文件的版本号:

  1. 次版本号 --minor_version 前2个字节用于表示次版本号

例如:00 00

  1. 主版本号 --major_version 后2个字节用于表示主版本号

例如: 00 34

这个版本号随着jdk版本的不同而表示不同版本的范围。Java的版本号是从45开始的

如果class的版本号超过虚拟机的版本 会被拒绝执行。

JDK1.2 ----0X002E 46

JDK1.3 ----0X002F 47

JDK1.4 ----0X0030 48

JDK1.5 ----0X0031 49

JDK1.6 ----0X0032 50

JDK1.7 ----0X0033 51

JDK1.8 ----0X0034 52

2.3 常量池

CONSTANT_POOL_COUNT和CONSTANT_POOL

紧跟着魔数与版本号之后的是常量池入口,常量池简单理解为class文件的资源库。

  1. 它是class文件结构中与其他项目关联最多的数据类型

  2. 是占用class文件空间最大的数据项目之一

  3. 是在文件中第一个出现的表类型数据项目。

常量池的数量是不固定,所以在常量池的入口需要放置一个u2类型的数据,代表常量池的计数值CONSTANT_POOL_COUNT。

CONSTANT_POOL_COUNT 从1开始计数的。 class文件结构中只有常量池的容量计数是从1开始的。第0项腾出来满足后面某些指向常量池的索引值的数据,在特定的情况下需要表达“不引用任何一个常量池项目” 把索引值的第0项留给JVM自己用。

CONSTANT_POOL是没有索引值为0的入口的,但是在CONSTANT_POOL_COUNT缺失的第0项也是要被计算在内的。

比如CONSTANT_POOL 中有14项 那么CONSTANT_POOL_COUNT的数值就是15

常量池中主要存放两大类常量:

  1. 字面量: 比较接近java语言层面的常量的概念 比如 字符串 被final关键字声明的常量值。

  2. 符号引用: 属于编译原理方面的概念 包括三项。

    • 类和接口的全名

    • 字段的名称和描述符

    • 方法的名称和描述符

在加载class文件的时候,是进行动态连接的。在class文件中不会保存各个方法和字段的最终内存布局信息。(需要经过转换) 当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或者运行时解析并翻译到具体的内存地址中。

CONSTANT_POOL_COUNT 占2个字节 本例中为0x22 转换成十进制为34 说明常量池中有31个常量 ----从1开始计数 其他集合类型均从0开始。 索引值为1-33 第0项常量具有特殊意义。

CONSTANT_POOL 表示的是类型数据集合,在该常量池中,每一项常量都是一个表 共有14种 -----JDK1.7版本,这14种结构的表都是不相同的结构数据。14个表都有一共同的特点,都是由u1的标志位开始的,可以通过这个标志位来判断这个常量属于哪种常量的类型。

在这里插入图片描述

2.4 access_flag

用于表示对该类或接口的访问权限以及该类或接口的属性

在这里插入图片描述

2.5 this_class

该this_class 项目的值 必须是constant_pool表中的有效索引,该constant_pool索引处的条目必须是表示此文件定义的类或接口 CONSTANT_Class_info 结构class

2.6 super_class

必须是constant_pool表中的有效索引, 如果super_class的值不为0 则constant_pool中的条目必须为CONSTANT_Class_info 结构 这个结构表示此类的文件定义的类的直接超类。直接超类不能在其classfile结构的access_flag项中设置 ACC_FINAL 标志。

其实要描述的意思就是说 如果superclass指代的超类,那么它就不能被final修饰。

2.8 常量池详细解析常量类型

总共有18个编号的常量类型。

编号1: CONSTANT_UTF8_INFO

TAG1 ------占用一个空间字节

Length: utf-8字符串占用的字节数

Bytes 长度为length字符串

用于表示utf-8的编码的字符串

编号3 CONSTANT_integer_info

Tag3

Bytes 4个字节 Big_Endian(高位在前) 存储int类型的值

编号4 CONSTANT_float_info

Tag4

Bytes 4个字节 Big_Endian(高位在前) 存储float类型的值

编号5 CONSTANT_long_info

Tag5

Bytes 8个字节 Big_Endian(高位在前) 存储long类型的值

编号6 CONSTANT_double_info

Tag6

Bytes 8个字节 Big_Endian(高位在前) 存储double类型的值

编号7 CONSTANT_Class_info

Tag7

Index 2个字节 指向类的全限定名的项的索引

类和接口符号引用

编号8 CONSTANT_String_info

Tag8

Index 2个字节 指向字符串的字面量的索引

编号9 CONSTANT_Fieldref_info

Tag9

Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项

Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项

字段的符号引用

编号10 CONSTANT_Methodref_info

Tag10

Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项

Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项

类中方法的符号引用

编号11 CONSTANT_InterfaceMethodref_info

Tag11

Index 2个字节 指向声明字段的类或接口的描述符 CONSTANT_Class_info的索引项

Index 2个字节 指向字段描述符CONSTANT_NameAndType的索引项

接口中方法的符号引用

编号12 CONSTANT_NameAndType

Tag12

Index 2个字节 指向该字段或方法名称常量项的索引
Index 2个字节 指向该字段或方法描述符常量项的索引

字段或方法的符号引用

编号15 CONSTANT_MethodHandler_info

Tag15

Reference_kind 1个字节 1-9之间的一个值 决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为

Reference_index 2个字节 对常量池的有效索引。

表示方法句柄

编号16 CONSTANT_MethodType_info

Tag16

Descriptor_index 2个字节 指向UTF8_info 结构表示的方法描述符

编号18CONSTANT_InvokeDynamic_info

Tag18

Bootstrap_method_attr_index: 2个字节 当前class文件中引导方法表的bootstrap_methods[] 数组的有效索引

Name_and_type_index: 2个字节 指向NameAndType_info 表示方法名和方法描述符。

表示动态方法的调用点。

2.8.1 案例解析

在这里插入图片描述

常量池中有33个常量信息,第一个是Constant_methodref_info 为方法引用信息

在这里插入图片描述

#6类名 指向常量池第六个的信息 —Object的类的信息,

#20名字和描述符 CONSTANT_NameAndType_info

NameAndType_info
在这里插入图片描述

当我们看到init字样的时候,是一个构造方法

当我们看到()V

() 表示方法没有参数

V 表示void 方法没有返回值。

整个常量池在classfile中的表示
cafe babe 0000 0034 0022 0a00 0600 1409
0015 0016 0800 170a 0018 0019 0700 1a07
001b 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 124c 6f63
616c 5661 7269 6162 6c65 5461 626c 6501
0004 7468 6973 0100 134c 636f 6d2f 6b65
7669 6e2f 5465 7374 6a76 6d3b 0100 046d
6169 6e01 0016 285b 4c6a 6176 612f 6c61
6e67 2f53 7472 696e 673b 2956 0100 0461
7267 7301 0013 5b4c 6a61 7661 2f6c 616e
672f 5374 7269 6e67 3b01 000a 536f 7572
6365 4669 6c65 0100 0c54 6573 746a 766d
2e6a 6176 610c 0007 0008 0700 1c0c 001d
001e 0100 0a48 656c 6c6f 2057 6f72 6407
001f 0c00 2000 2101 0011 636f 6d2f 6b65
7669 6e2f 5465 7374 6a76 6d01 0010 6a61
7661 2f6c 616e 672f 4f62 6a65 6374 0100
106a 6176 612f 6c61 6e67 2f53 7973 7465
6d01 0003 6f75 7401 0015 4c6a 6176 612f
696f 2f50 7269 6e74 5374 7265 616d 3b01
0013 6a61 7661 2f69 6f2f 5072 696e 7453
7472 6561 6d01 0007 7072 696e 746c 6e01
0015 284c 6a61 7661 2f6c 616e 672f 5374
7269 6e67 3b29 5600 2100 0500 0600 0000
0000 0200 0100 0700 0800 0100 0900 0000
2f00 0100 0100 0000 052a b700 01b1 0000
0002 000a 0000 0006 0001 0000 0003 000b
0000 000c 0001 0000 0005 000c 000d 0000
0009 000e 000f 0001 0009 0000 0037 0002
0001 0000 0009 b200 0212 03b6 0004 b100
0000 0200 0a00 0000 0a00 0200 0000 0500
0800 0600 0b00 0000 0c00 0100 0000 0900
1000 1100 0000 0100 1200 0000 0200 13
常量池第一行

从0a开始,0a对应的是十进制的10,映射到常量表中的Tag10的标志:CONSTANT_Methodref_info;

0006 表示 index 2个字节,指向声明方法的类或接口的描述符#6 CONSTANT_Class_info的索引项。指向#6行的内容。

0014 14 转成十进制是20,指向#20行的内容是,表示的index2个字节 指向字段描述符CONSTANT_NameAndType_info的索引项

常量池第二行

从09开始,09对应的是十进制的9,映射到常量表中的Tag9的标志: CONSTANT_Fieldref_info;

0015 表示 index 2个字节,指向声明方法的类或接口的描述符#21 CONSTANT_Class_info的索引项。指向#21行的内容。

0016 16 转成十进制是22,指向#22行的内容是,表示的index2个字节 指向字段描述符CONSTANT_NameAndType_info的索引项

常量池第三行

从08开始,08对应的是十进制的8,映射到常量表中的Tag8的标志: CONSTANT_String_info;

0017 表示Index 2个字节 指向字符串的字面量的索引#23 CONSTANT_UTF8_INFO的索引,指向#28行

常量池第五行

从07开始,07对应的是十进制的7,映射到常量表中的Tag7的标志:CONSTANT_Class_info;

001a 表示Index 2个字节 指向类的全限定名的项的索引(类和接口符号引用)#26 CONSTANT_UTF8_INFO的索引,指向#26行

常量池第七行

从01开始,01对应的是十进制的1,映射到常量表中的Tag1的标志:CONSTANT_UTF8_INFO;

0006 表示utf-8字符串占用的字节数为6

3c696e69743e 表示utf-8的编码的字符串 <init>

的是十进制的8,映射到常量表中的Tag8的标志: CONSTANT_String_info;

0017 表示Index 2个字节 指向字符串的字面量的索引#23 CONSTANT_UTF8_INFO的索引,指向#28行

常量池第五行

从07开始,07对应的是十进制的7,映射到常量表中的Tag7的标志:CONSTANT_Class_info;

001a 表示Index 2个字节 指向类的全限定名的项的索引(类和接口符号引用)#26 CONSTANT_UTF8_INFO的索引,指向#26行

常量池第七行

从01开始,01对应的是十进制的1,映射到常量表中的Tag1的标志:CONSTANT_UTF8_INFO;

0006 表示utf-8字符串占用的字节数为6

3c696e69743e 表示utf-8的编码的字符串 <init>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值