深入理解java虚拟机:类文件结构(1)
创始人
2024-01-31 09:59:31
0

文章目录

  • 1. 概述
  • 2. Class类文件的结构
    • 2.1 魔数与Class文件的版本
    • 2.2 常量池
    • 2.3 访问标志
    • 2.4 类索引、父类索引与接口索引集合
    • 2.5 字段表集合

1. 概述

如果计算机的CPU指令集就只有x86一种,操作系统就只有Windows一种,那也许就不会有Java语言的出现。Java在刚刚诞生之时曾经提出过一个非常著名的宣传口号:“一次编写,到处运行(Write Once,Run Anywhere)”,这句话充分表达了软件开发人员对冲破平台界限的渴求。在无时无刻不充满竞争的IT领域,不可能只有Wintel存在,我们也不希望只有Wintel存在,各种不同的硬件体系结构和不同的操作系统定将会长期并存发展。“与平台无关”的理想最终实现在操作系统的应用层上:Sun公司及其他虚拟机提供商发布了许多可以运行在各种不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码,从而实现程序的“一次编写,到处运行”。

在这里插入图片描述

2. Class类文件的结构

img

Class的结构不想XML等描述语言,由于它没有任何分隔符号,所以上述的数据项,无论是顺序还是数量,都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何,都不允许改变

2.1 魔数与Class文件的版本

每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是用于确定这个文件是否为一个能被虚拟机接收的 Class 文件。

这个魔数为:0xCAFEBABE

第5和第6个字节是次版本(minor_version)

第7和第8个字节是主版本号(major_version)

查看一个class文件,结果如下:
在这里插入图片描述

下表列举了从JDK1.1到1.7之间,主流JDK版本编译器输出的默认和可支持的Class文件版本号。

在这里插入图片描述

2.2 常量池

紧接着主次版本号之后的是常量池入口,是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时也是Class文件中第一个出现的表类型数据项目

由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项2类型的数据,代表常量池容量计数值(constant_pool_count)。与Java中的语言习惯不一样的是,这个容量计数是从1而不是0开始的,如图6-3所示,常量池容量(偏移地址:0x00000008)为十六进制数0x0016,即十进制的22,这就代表常量池中有21项常量,索引值为1~21。制定Class文件格式规范时,将第0项常量空出来是有特殊考虑的,这样做是为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的意思,这种情况就可以把索引值置为0来表示。Class文件结构中只有常量池的容量计数是从1开始的,对于其他集合类型,包括接口索引集合、字段表集合、方法表集合等的容量计数都与一般习惯相同,是从0开始的。

第9和第10个字节是常量池计数值,因为常量池中常量不固定,所以在常量池入口放置一项u2类型的数据
在这里插入图片描述
常量池中的每一项常量都是一个表,共有11种结构“各不相同的表结构数据,这11种表都有一个共同的特点,就是表开始的第一位是一个ul类型的标志位(tag,取值为1至12,缺少标志为2的数据类型),代表当前这个常量属于哪种常量类型,11种常量类型所代表的具体含义如下所示
在这里插入图片描述
图6-3,常量池的第1个常量是07,从表6-1查表可知道该常量类型为CONSTANT_Class_info,表示类或接口的符号引用。

Oracle公司已经为我们准备好一个专门用于分析Class文件字节码的工具:javap

javap -verbose [class文件名]

举个例子,可以看到一共18个常量全部显示

C:\>javap -verbose TestClass
public class com.monk.TestClass                minor version: 0                             major version: 55                            flags: (0x0021) ACC_PUBLIC, ACC_SUPER        this_class: #3                          // com/monk/TestClass           super_class: #4                         // java/lang/Object             interfaces: 0, fields: 1, methods: 2, attributes: 1                     
Constant pool:                                                            #1 = Methodref          #4.#15         // java/lang/Object."":()V#2 = Fieldref           #3.#16         // com/monk/TestClass.m:I       #3 = Class              #17            // com/monk/TestClass           #4 = Class              #18            // java/lang/Object             #5 = Utf8               m                                              #6 = Utf8               I                                              #7 = Utf8                                                        #8 = Utf8               ()V                                            #9 = Utf8               Code                                           #10 = Utf8               LineNumberTable                                #11 = Utf8               inc                                            #12 = Utf8               ()I                                            #13 = Utf8               SourceFile                                     #14 = Utf8               TestClass.java#15 = NameAndType        #7:#8          // "":()V#16 = NameAndType        #5:#6          // m:I#17 = Utf8               com/monk/TestClass#18 = Utf8               java/lang/Object

这里我们将11中常量项的结构定义总结成下表

在这里插入图片描述在这里插入图片描述

2.3 访问标志

在常量池结束之后,紧接着的2个字节代表访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息,包括:这个Clss是类还是接口:是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final,等等。具体的标志位及标志的含义见下表
在这里插入图片描述

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

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

例如上面使用javap命令生成的类信息中找出对应的类和父类的常量

  #3 = Class              #17            // com/monk/TestClass           #4 = Class              #18            // java/lang/Object        

2.5 字段表集合

字段表field info用于描述接口或类中声明的变量。字段(field)包括了类级变量或实例级变量,但不包括在方法内部声明的变量。我们可以想一想在Java中描述一个字段可以包含什么信息?可以包括的信息有:字段的作用域(public、private、protected修饰符)、是类级变量还是实例级变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否序列化(transient修饰符)、字段数据类型(基本类型、对象、数组)、字段名称。这些信息中,各个修饰符都是布尔值,要么有某个修饰符,要么没有,很适合使用标志位来表示。而字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。下表中列出了字段表的最终格式。

在这里插入图片描述

这里也有access_flags,与前面提到的访问标志类似
在这里插入图片描述
name_indexdescriptor_index:字段的简单名称和描述符

名称很好识别,比如已/符号出现的一些名称,比较复杂的是描述符,下面我们看看描述符。描述符的
作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示,详见下表

在这里插入图片描述
对于数组类型,每一维度将使用一个前置的[字符来描述,如一个定义为java,lang.String类型的二维数组,将被记录为:[[Ljava/lang/String;,一个整型数组int[]将被记录为[I

对于方法来说,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号()中,比如方法void inc()的描述符为()V,方法 java.lang.String toString()的描述符()Ljava/lang/String;比如第8个常量和第12个常量的描述符表示:

 #8 = Utf8               ()V      // 表示空参空返回                                      #9 = Utf8               Code                                           #10 = Utf8               LineNumberTable                                #11 = Utf8               inc#12 = Utf8               ()I		// 表示空参返回Integer

相关内容

热门资讯

美国佐治亚州发生一起枪击事件 ...   当地时间1月2日,美国佐治亚州东梅肯舒林大道于当日15时40分左右发生一起枪击事件,执法人员发现...
“送到家门、直发个人” 各地打...   “3600元到账!小小年纪就领上‘年薪’啦。”2025年末,北京市石景山区的宋女士,惊喜地发现育...
多地新年迎降雪 各部门协同除冰...   央视网消息:1月1日早晨,江苏宿迁、淮安等地迎来了2026年的第一场雪。皑皑白雪为城市披上银装,...
1月1日起正式施行 国家公园法...   一问到底丨1月1日起正式施行 国家公园法将如何助力美丽中国建设?  我国首部关于国家公园的专门立...
这组漫画“照妖镜”直指“山姆大...   新华社北京1月2日电“美利坚合众国似乎‘天意注定’要假‘自由’之名给美洲带来苦难。”委内瑞拉民族...
台湾写真:“小三通”25周年 ...   “这是金门的新码头大楼,到时候可以服务更多经‘小三通’航线来金门旅游的大陆朋友,金门民众去大陆也...
焦点访谈丨“小家”热气腾腾 “...   新年前夕,国家主席习近平发表了二〇二六年新年贺词,回顾极不寻常的过去5年,凝望过去一年走过的不平...
新疆冰雪经济奏响新年“热”旋律   新年伊始,新疆各大滑雪场同步开启新雪季运营大幕,以升级后的设施与优质雪质喜迎八方雪友,银装素裹的...
千年古镇杨柳青的跨年奇旅   元旦假期期间,天津市西青区杨柳青古镇披上了节日盛装。在这里,每天都上演传统与现代交融的新年盛宴,...
消息人士称沙特空袭也门南方过渡...   当地时间1月2日,也门南方过渡委员会消息人士称,该委员会在哈德拉毛地区的目标遭沙特空袭。