JVM的故事——类文件结构

类文件结构

文章目录


一、概述

计算机是只认由0、1组成的二进制码的,不过随着发展,我们编写的程序可以被编译成与指令集无关、平台中立的一种格式。

二、无关性基石

对于不同平台和不同平台的Java虚拟机,都支持一种程序存储格式---字节码。Java虚拟机并不与Java这个语言绑定,它只与class文件绑定。任何语言编译后生成的class文件都可以在Java虚拟机上运行。

三、Class类文件的结构

Java语言一直保持着良好的向后兼容性,Class文件结构的稳定功不可没。在Java语言经历了许多的改进和更新,Class文件的结构和功能几乎没有变化,只有一些新增和补充内容。

任何一个class文件都对应着唯一的一个类或者接口的定义信息,但并不一定每个类或者接口都有着对应的class文件(可以动态生成,直接进入类加载器中)。

Class文件是一组以8个字节为基础单位的二进制流,中间没有任何的分隔符。当遇到大于8个字节的空间存储时,会按照高位在前把变量分割成若干个8个字节进行存储。Class文件存储数据只通过两种结构:无符号数和表。u1、u2、u8代表1、2、8个字节的无符号数。表是由多个无符号数或者其它表构成的结构,表的命名都习惯性地以"_info"结尾。整个class文件也可以看成一张表,表的组成结构如下图。

1、魔数与Class文件的版本

每个class文件的头四个字节是魔数,这是用来辨别该class文件是否可以被虚拟机接受。许多类型的文件都有魔数,例如gif、jpg等。使用魔数比使用后缀名辨别文件格式更加安全,因为后缀名是可以更改的。文件格式的魔数由制定者自己定义,class文件的魔数是0XCAFEBABE

紧接着魔数后面的四个字节是class文件的版本号定义,第五六字节定义的是次版本号(minor version),第七八字节定义的是主版本号(major version)。Java的class文件版本号是从45开始的,每个Java的大版本发布,主版本号就加一。高版本JDK可以兼容低版本的class文件。

分析此图,由5、6位是0x0000可以得出次版本号为0,由7、8位是0x0032可以得出主版本号是50,所以该class文件版本号是50.0,对应的JDK版本应该是JDK1.6,JDK1.6能支持版本号为45.0-50.65535的class文件。

2、常量池

在主次版本号之后的就是常量池,它是class文件中的资源仓库,占用着大量的数据,也是第一个出现的表类型数据项。

常量池的入口需要放置一个u2类型的数据,来表示有多少常量。这个容量的计数是从1开始而不是从0开始的

如此图,可以看见偏移地址0x00000008处,值为0x0016,即常量池的容量为22,那么就有21个常量,索引为1-21。把0空出来是为了表达不引用任何一个常量来设计的。不过除了常量池的其它集合,索引还都是从0开始的。

常量池中的每一个常量都是一个表,截止JDK13,常量表中一共有17种不同类型的常量。这些常量表都有一个相同的特点,就是它们的第一位是u1类型的标志位。

下面开始对常量进行分析

第0x00000008偏移位说明了有21个常量。从0x0000000A开始,0x07是第一个常量的标志位(tag),根据6-3常量池的项目类型可以得知这是一个类或接口的符号引用

根据表6-4就可以知道接下来两个字节要表示这个常量的name_index,即常量池的索引值。它指向一个CONSTANT_Utf8_info类型常量,这个常量代表类的全限定名。从偏移位0x0000000B开始,可以知道该常量的索引值为0x0002,即指向第二个常量。

那么就从0x0000000D开始,看第二个常量,标志位为0x01,由表6-3可知该常量是UTF-8编码的字符串。这里使用的是UTF-8缩略编码,区别是:从'\u0001'到'\u007f'之间的字符(相当于1~127的ASCII码)的缩略编码使用一个字节表示, 从'\u0080'到'\u07ff'之间的所有字符的缩略编码用两个字节表示,从'\u0800'开始到'\uffff'之间的所有字符 的缩略编码就按照普通UTF-8编码规则使用三个字节表示。(其实就是省略所有前面的0)

由表6-5,可以从偏移位0x0000000E开始,看该字符串的length,为0x001D,即29。往后29 个字节正好都在1~127的ASCII码范围以内,内容为"org/fenixsoft/clazz/TestClass

可以使用javap指令输出常量表,由输出的常量对比,发现分析的两个常量都是正确的。

像常量表中的如"I""V"""这些,在程序中并没有,它们是由编译器自动生成的,是用来描述一些不方便用固定字节描述的内容的。

3、访问标志

在常量池结束后,后面两个字节是访问标志。访问标志代表着该类或接口的访问信息

比如6-1中的代码,TestJava是一个普通的Java类。它被public修饰,并且使用了JDK1.2之后的编译器进行编译,所以它的access_flag应该是0x0001|0x0020=0x0021


相关推荐
流星5211221 天前
GC 如何判断对象该回收?从可达性分析到回收时机的关键逻辑
java·jvm·笔记·学习·算法
JanelSirry1 天前
我的应用 Full GC 频繁,怎么优化?
jvm
JH30731 天前
jvm,tomcat,spring的bean容器,三者的关系
jvm·spring·tomcat
DKPT1 天前
JVM直接内存和堆内存比例如何设置?
java·jvm·笔记·学习·spring
siriuuus1 天前
JVM 垃圾收集器相关知识总结
java·jvm
小满、1 天前
什么是栈?深入理解 JVM 中的栈结构
java·jvm·1024程序员节
百花~2 天前
JVM(Java虚拟机)~
java·开发语言·jvm
每天进步一点点dlb2 天前
JVM中的垃圾回收算法和垃圾回收器
jvm·算法
漫漫不慢.2 天前
蓝桥杯-16955 岁月流转
java·jvm·蓝桥杯
boy快快长大3 天前
【JVM】线上JVM堆内存报警,占用超90%
jvm