详解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,一经查实,立即删除!

相关文章

【Python学习】 - sklearn学习 - 交叉验证中的常用函数

首先调入库:from sklearn.model_selection import train_test_split train_test_split是交叉验证中常用的函数,功能是从样本中随机的按比例选取train data和test data。注意输出的参数对应的次序。 语法: X_train,X_test, y_train, y_test…

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 概述 机器学习应用程序已经无处不在…

【Python学习】 - Pandas学习 sort_value( ),sort_index( )排序函数的区别与使用

按索引对DataFrame或Series进行排序(注意ascendingfalse的意思是按照降序排序,若不写参数则默认升序排序) DataFrame的构造函数默认参数是(值,列名,行索引),行索引不填则默认0,1,2,3这样? In …

powerdesign 缩写AK,PK,IX,CK,FK,DF,UQ

PK - Primary Key IX - Non-Unique Index AK - Unique Index (AX should have been AK (Alternate Key)) CK - Check Constraint DF - Default Constraint FK - Foreign Key UQ - Unique Constraint

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

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

【PAT - 甲级1045】Favorite Color Stripe(30分)(dp,LIS类问题)

题干: Eva is trying to make her own color stripe out of a given one. She would like to keep only her favorite colors in her favorite order by cutting off those unwanted pieces and sewing the remaining parts together to form her favorite color s…

机器学习编译第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 通…

迁移TFS 2012服务至新的电脑硬件

迁移TFS 2012的时候碰到一些问题, 中文记录很少, 英文的记录也比较零散. 这里记录最直接和简单的方法. 环境: 1. 公司域环境, 所有TFS用户都是公司域帐户. 2. TFS从一台服务器转移至另一台服务器. 都加入了公司域. 机器名分别为PC1和PC2. 域内不能有同名的电脑。 准备两台…

【PAT - 甲级1005】Spell It Right (20分) (递归输出,水题)

题干: Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English. Input Specification: Each input file contains one test case. Each case occupies one line which contain…