详解Class类文件的结构(上)

前言

相信搞Java开发的同学都经常会接触到Class类文件,了解了JVM虚拟机之后也会大量接触到class字节码,那么它到底是什么样的文件?内部由什么构成?虚拟机又是如何去识别它的?这篇文章就来学习一下Class类文件的结构。

ps:我在面试蚂蚁的时候被问到过这个问题!你没看错,面试也有可能会问。

一、什么是Class文件

Class文件又称字节码文件,一种二进制文件,它是由某种语言经过编译而来,注意这里并不一定是Java语言,还有可能是Clojure、Groovy、JRuby、Jython、Scala等,Class文件运行在Java虚拟机上。Java虚拟机不与任何一种语言绑定,它只与Class文件这种特定的二进制文件格式所关联。

虚拟机具有语言无关性,它不关心Class文件的来源是何种语言,它只关心Class文件中的内容。Java语言中的各种变量、关键字和运算符号的语义最终都是由多条字节码命名组合而成的,因此字节码命令所能提供的语义描述能力比Java语言本身更加强大。

二、Class文件的结构

虚拟机可以接受任何语言编译而成的Class文件,因此也给虚拟机带来了安全隐患,为了提供语言无关性的功能就必须做好安全防备措施,避免危险有害的类文件载入到虚拟机中,对虚拟机造成损害。所以在类加载的第二大阶段就是验证,这一步工作是虚拟机安全防护的关键所在,其中检查的步骤就是对class文件按照《Java虚拟机规范》规定的内容来对其进行验证。

1.总体结构

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,就按照高位在前的方式分割成若干个8位字节进行存储。

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

  • 无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节、8个字节的无符号数,无符号数可以来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性的以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表,它的数据项构成如下图。

2.魔数(Magic Number)

每一个Class文件的头4个字节成为魔数(Magic Number),它的唯一作用是确定这个文件是否是一个能被虚拟机接收的Class文件。很多文件存储标准中都是用魔数来进行身份识别,比如gif、png、jpeg等都有魔数。使用魔数主要是来识别文件的格式,相比于通过文件后缀名识别,这种方式准确性更高,因为文件后缀名可以随便更改,但更改二进制文件内容的却很少。Class类文件的魔数是Oxcafebabe,cafe babe?咖啡宝贝?至于为什么是这个, 这个名字在java语言诞生之初就已经确定了,它象征着著名咖啡品牌Peet's Coffee中深受欢迎的Baristas咖啡,Java的商标logo也源于此。

3.文件版本(Version)

在魔数后面的4个字节就是Class文件的版本号,第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布主版本号向上加1(JDK1.0~1.1使用的版本号是45.0~45.3),比如我这里是十六进制的Ox0034,也就是十进制的52,所以说明该class文件可以被JDK1.8及以上的虚拟机执行,否则低版本虚拟机执行会报java.lang.UnsupportedClassVersionError错误。

4.常量池(Constant Pool)

在主版本号紧接着的就是常量池的入口,它是Class文件结构中与其他项目关联最多的数据类型,也是占用空间最大的数据之一。常量池的容量由后2个字节指定,比如这里我的是Ox001d,即十进制的29,这就表示常量池中有29项常量,而常量池的索引是从1开始的,这一点需要特殊记忆,因为程序员习惯性的计数法是从0开始的,而这里不一样,所以我这里常量池的索引范围是1~29。设计者将第0项常量空出来是有目的的,这样可以满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义。

通过javap -v命令反编译出class文件之后,我们可以看到常量池的内容

常量池中主要存放两大类常量:字面量符号引用。比如文本字符、声明为final的常量值就属于字面量,而符号引用则包含下面三类常量:

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

在之前的文章(详谈类加载的全过程)中有详细讲到,在加载类过程的第二大阶段连接的第三个阶段解析的时候,会将常量池中的符号引用替换为直接引用。相信很多人在开始了解那里的时候也是一头雾水,作者我也是,当我了解到常量池的构成的时候才明白真正意思。Java代码在编译的时候,是在虚拟机加载Class文件的时候才会动态链接,也就是说Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法获得真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中

常量池中每一项常量都是一张表,这里我只找到了JDK1.7之前的常量池项目类型表,见下图。

  • 常量池项目类型表:

  • 常量池常量项的结构总表:

比如我这里测试的class文件第一项常量,它的标志位是Ox0a,即十进制10,即表示tag为10的常量项,查表发现是CONSTANT_Methodref_info类型,和上面反编译之后的到的第一个常量是一致的,Methodref表示类中方法的符号引用。查上面《常量池常量项的结构总表》可以看到Methodref中含有3个项目,第一个tag就是上述的Ox0a,那么第二个项目就是Ox0006,第三个项目就是Ox000f,分别指向的CONSTANT_Class_info索引项和CONSTANT_NameAndType_info索引项为6和15,那么反编译的结果该项常量指向的应该是#6和#15,查看上面反编译的图应证我们的推测是对的。后面的常量项就以此类推。

这里需要特殊说明一下utf8常量项的内容,这里我以第29项常量项解释,也就是最后一项常量项。查《常量池常量项的结构总表》可以看到utf8项有三个内容:tag、length、bytes。tag表示常量项类型,这里是Ox01,表示是CONSTANT_Utf8_info类型,紧接着的是长度length,这里是Ox0015,即十进制21,那么再紧接着的21个字节都表示该项常量项的具体内容。特别注意length表示的最大值是65535,所以Java程序中仅能接收小于等于64KB英文字符的变量和变量名,否则将无法编译

5.访问标志(Access Flags)

在常量池结束后,紧接着的两个字节代表访问标志(Access Flags),该标志用于识别一些类或者接口层次的访问信息,其中包括:Class是类还是接口、是否定义为public、是否定义为abstract类型、类是否被声明为final等。

访问标志表

标志位一共有16个,但是并不是所有的都用到,上表只列举了其中8个,没有使用的标志位统统置为0,access_flags只有2个字节表示,但是有这么多标志位怎么计算而来的呢?它是由标志位为true的标志位值取或运算而来,比如这里我演示的class文件是一个类并且是public的,所以对应的ACC_PUBLIC和ACC_SIPER标志应该置为true,其余标志不满足则为false,那么access_flags的计算过程就是:Ox0001 | Ox0020 = Ox0021

篇幅原因,未完待续......

参考文献:《深入理解Java虚拟机》

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Coursera自动驾驶课程第19讲:Mapping for Planning

在第18讲 《Coursera自动驾驶课程第18讲:The Planning Problem》 我们对自动驾驶中的规划问题有了一个全面的了解,理解了规划问题中的约束和目标;同时我们还讨论了如何分层如解决规划问题(任务规划、行为规划、路径规划和速度曲线…

详解Class类文件的结构(下)

本文继续使用上次的Test.class文件,它是由下面单独的一个类文件编译而成的,没有包。 6. 索引(Index) 索引又分类索引、父类索引和接口索引集合,类索引(this_class)和父类索引(super…

自动驾驶开源软件和算法库

1. Carla(自动驾驶开源仿真软件) github:https://github.com/carla-simulator/carladoc:https://carla.readthedocs.io/en/latest/website:http://carla.org/Bounding boxes:https://carla.readthedocs.io/…

Coursera自动驾驶课程第20讲:Mission Planning in Driving Environments

在第19讲《Coursera自动驾驶课程第19讲:Mapping for Planning》 我们学习了自动驾驶中两种环境建图方法:占用网格图(occupancy grid map) 和 高清地图(high-definition road map)。 在本讲中,我…

Java实例化对象过程中的内存分配

问题引入 这里先定义一个很不标准的“书”类,这里为了方便演示就不对类的属性进行封装了。 class Book{String name; //书名double price; //价格public void getInfo(){System.out.println("name:"name";price:"price);} } 在这个类中定义了两个属…

【Python学习】 - sklearn学习 - KNN

前言: 针对一个完整的机器学习框架目前还没有总结出来,所以目前只能总结每一个单独的算法。由于现在研究的重点是算法,所以对于数据的处理,数据的分析和可视化呈现,在现阶段并不进行展示(这样容易陷入纠结…

重读经典:《End-to-End Object Detection with Transformers》

DETR 论文精读【论文精读】这一次朱毅博士给大家精读的论文是 DETR,是目标检测领域里程碑式的一个工作,文章收录于 ECCV20 。DETR 是 Detection Transformer 的缩写,作者使用 Transformer 简化了目标检测流程,不再需要进行 NMS&am…

Execute SQL Task 参数和变量的映射

Execute SQL Task能够执行带参数的SQL查询语句或存储过程(SP),通过SSIS的变量(Variable)对参数赋值。对于不同的Connection Manager,在Task中需要使用不同的符号(Parameter marker)来…

【Python学习】 - 手写数字识别 - python读入mnist数据集的多种方法

写在前面: 其实网上有很多读入mnist数据的代码,但是都是比较麻烦冗长的函数,本篇文章介绍几种不算很麻烦的,借用库函数读入数据的方法。 方法1: 方法2: 方法3:

Coursera自动驾驶课程第21讲:Dynamic Object Interactions

在第20讲《Coursera自动驾驶课程第20讲:Mission Planning in Driving Environments》 我们学习了任务规划中常用的三种图搜索算法:Breadth First Search、Dijkstra 和 A* 搜索。 在本讲中我们将讨论运动规划器中使用的方法,以处理动态物体和…

sql server 数据库忘记sa账户密码/ 无管理员账户解决办法

一、计算机超级管理员账户有数据库的管理员权限 用管理员账户登录数据库,直接修改sa账户密码即可。 二、数据库中没有管理员权限的账户 SQL Server 2005/2008提供了针对该情况的更好的灾难恢复方法,无需侵入master数据库,不会对master数据库…

机器学习编译第1讲:机器学习编译概述

MLC-机器学习编译-第一讲-机器学习编译概述课程主页:https://mlc.ai/summer22-zh/ 文章目录1.0 概述1.1 什么是机器学习编译1.2 为什么学习机器学习编译1.3 机器学习编译的关键要素1.3.1 备注:抽象和实现1.4 总结1.0 概述 机器学习应用程序已经无处不在…

重读经典:《The Craft of Research(1)》

跟读者建立联系【研究的艺术一】这一次李沐博士给大家精读的是一本关于论文写作的书籍。这本书总共包含四个大的章节,本期视频李沐博士介绍的是第一个章节:Research,Researchers,and Readers。 0. 前言 视频开头,李沐…

机器学习编译第2讲:张量程序抽象

02 张量程序抽象 【MLC-机器学习编译中文版】课程主页:https://mlc.ai/summer22-zh/ 文章目录2.1 元张量函数2.2 张量程序抽象2.2.1 张量程序抽象中的其它结构2.3 张量程序变换实践2.3.1 安装相关的包2.3.2 构造张量程序2.3.3 编译与运行2.3.4 张量程序变换2.3.5 通…

详解自动驾驶仿真数据集 SHIFT:A Synthetic Driving Dataset for Continuous Multi-Task Domain Adaptation

SHIFT:A Synthetic Driving Dataset for Continuous Multi-Task Domain Adaptation本文介绍一个新的自动驾驶仿真数据集:SHIFT,论文收录于 CVPR2022。适应连续变化的环境是自动驾驶系统一直以来要面临的挑战。然而,目前现有的图像…

TFS下的源代码控制

以下主要描述了: TFS源代码控制系统的基本场景如何把一个项目添加到源代码管理中如何与服务器同步如何做Check-In如何做分支与合并什么是上架与下架 我们知道工作项是项目管理的基本元素,但是一个项目的成功,光有工作项还是不够的。工作项说…

地平线:面向规模化量产的智能驾驶系统和软件开发

导读 7月27日,地平线在智东西公开课开设的「地平线自动驾驶技术专场」第3讲顺利完结,地平线智能驾驶应用软件部负责人宋巍围绕 《面向规模化量产的智能驾驶系统和软件开发》这一主题进行了直播讲解。本次分享主要分为以下4个部分: 1、智能驾驶…

重读经典(CLIP上):《Learning Transferable Visual Models From Natural Language Supervision》

CLIP 论文逐段精读【论文精读】这一次朱毅博士给大家精读的论文是 CLIP,来自于 OpenAI,是图像文本多模态领域一个里程碑式的工作。 CLIP 的影响力可见一斑,如果按照沐神之前讲的如何判断一个工作的价值来说,CLIP 应该就是 1001001…

TFS准备(一)

一、TFS概念: TFS全称Team FoundationServer,是应用程序生命周期管理的服务端,功能包括如图功能:源代码管理,版本控制,团队开发协作,统一集成,测试管理等。 二、TFS安装要求&#…

重读经典(CLIP下):《Learning Transferable Visual Models From Natural Language Supervision》

上文链接:重读经典(CLIP上):《Learning Transferable Visual Models From Natural Language Supervision》 5. 实验 现在我们已经知道 CLIP 是如何进行预训练的以及作者为什么选用对比学习来预训练 CLIP,接下来我们就…