JVM虚拟机 - Class类文件结构
概述
Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎都是程序运行的必要数据。当遇到需要占用8位字节以上空间的数据项时,会按照高位在前的方式分割成若干个8位字节进行存储。
Class文件格式中只有两种数据类型:无符号数和表。
- 无符号数属于最基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
- 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,以“_info”结尾。表用来描述有层次关系的符合结构的数据。
整个Class文件本质上就是一张表。
无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,称这一系列连续的某一类型的数据位某一类型的集合。
class文件的组织结构
- 魔数
- 本文件的版本信息
- 常量池
- 访问标志
- 类索引
- 父类索引
- 接口索引集合
- 字段表集合
- 方法表集合
- 属性表集合
1. 魔数
每个Class文件的头4个字节称为魔数(Magic Number),唯一作用就是确定这个文件是否为一个能被虚拟机接受的Class文件。
魔数的表示是用16进制的数:0xCAFEBABE 来表示。
2. 本文件的版本信息
在魔数后面的4个字节存储的就是Class文件的版本号:
- 第5、第6个字节是次版本号(Minor Version)
- 第7、第8个字节是主版本号(Major Version)
Java版本号的计算
Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布主版本号向上加1。
高版本的JDK能向下兼容以前版本的Class文件,但不能兼容以后版本的Class文件,即使文件格式并未发生变化,虚拟机也拒绝执行超过其版本号的Class文件。
3. 常量池
3.1. 常量池的概念
常量池可以理解为Class文件之中的资源仓库,他是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型数据项目。
常量池中主要存放两大类常量:
- 字面量:接近于Java语言层面的常量概念,如文本字符串、声明为final的常量
- 符号引用:就是我们定义的各种名字,包括下面三类:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
3.2. 常量池的特点
常量池长度不固定
常量池的大小是不固定的,因此常量池开头放置一个u2类型的无符号数,用来存储当前常量池的容量。JVM根据这个值就知道常量池的头尾来。
这个无符号数从1开始,不是通常的从0开始
常量池中的常量由二维表来表示
常量池开头有个常量池容器计数器,接下来就全是一个个常量了,只不过常量都是由一张张二维表构成,除了记录常量的值以外,还记录当前常量的相关信息。
Class文件之中的资源仓库
Class文件结构中与其他项目关联最多的数据类型
占用Class文件空间最大的数据项目之一
3.3. 常量池中常量的类型
刚才介绍了,常量池中的常量大体上分为:字面值常量 和 符号引用。在此基础上,根据常量的数据类型不同,又可以被细分为14种常量类型。这14种常量类型都有各自的二维表示结构。每种常量类型的头1个字节都是tag,用于表示当前常量属于14种类型中的哪一个。
以CONSTANT_Class_info常量为例,它的二维表示结构如下:
CONSTANT_Class_info表:
| 类型 | 名称 | 数量 |
|---|---|---|
| u1 | tag | 1 |
| u2 | name_index | 1 |
tag表示当前常量的类型(当前常量为CONSTANT_Class_info,因此tag的值应为7,表示一个类或接口的全限定名);
name_index表示这个类或接口全限定名的位置。它的值表示指向常量池的第几个常量。它会指向一个CONSTANT_Utf8_info类型的常量,它的二维表结构如下:
CONSTANT_Utf8_info表:
| 类型 | 名称 | 数量 |
|---|---|---|
| u1 | tag | 1 |
| u2 | length | 1 |
| u1 | bytes | length |
CONSTANT_Utf8_info表示字符串常量;
tag表示当前常量的类型,这里应该是1;
length表示这个字符串的长度;
bytes为这个字符串的内容(采用缩略的UTF8编码)
4. 访问标志
访问标志(access_flags)占用两个字节,这个标志用于识别一些类或者接口层次的访问信息:
- 这个Class是类还是接口
- 是否定位为public类型
- 是否定义为abstract类型
- 是否被声名为final
- .......
访问标志中一共有16个标志位可用,当前只用了8个,其他要求一律为0。
5. 类索引、父类索引和接口索引集合
类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,接口索引集合(interfaces)是一组u2类型的数据的集合。
Class文件由这三项数据来确定这个类的继承关系:
类索引确定这个类的全限定名
父类索引用于确定这个类的父类的全限定名
除
java.lang.Object外,所有Java类的父类索引都不为0。接口索引集合描述这个类实现了哪些接口
被实现的接口按接口顺序从左到右排列在接口索引集合中。
类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的所引致指向一个类型为CONSTANT_Class_info的类描述符常量,该常量的bytes字段记录了本类、父类的全限定名。
由于一个类的接口可能有好多个,因此需要用一个集合来表示接口索引,它在类索引和父类索引之后。这个集合头两个字节表示接口索引集合的长度,接下来就是接口的名字索引。
6. 字段表集合
字段表用于描述接口或者类中生命的变量。字段包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。
| 类型 | 名称 | 数量 |
|---|---|---|
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
access_flags
字段的访问标志。
name_index
本字段名称的索引,指向一个CONSTANT_Class_info类型的常量,存储了本字段的名字等信息。
descriptor_index
字段和方法的描述符,用于描述本字段在Java中的数据类型等信息。
attributes_count
属性表集合的长度。
attributes
属性表集合。
全限定名
全限定名就是把类全名中的.替换成了/而已,为了使连续的多个全限定名之间不产生混淆,在使用时最后一般会加入一个;来表示全限定名结束。
简单名称
简单名称就是指没有类型和参数修饰的方法或者字段名称。
描述符
描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。
在描述符中:
- 基本数据类型以及代表无返回值的void类型都用一个大写字符来表示。
- 对象类型则用字符L加对象的全限定名来表示。
| 标识字符 | 含义 | 标识字符 | 含义 |
|---|---|---|---|
| B | 基本类型byte | J | 基本类型long |
| C | 基本类型char | S | 基本类型short |
| D | 基本类型double | Z | 基本类型boolean |
| F | 基本类型float | V | 特殊类型void |
| I | 基本类型int | L | 对象类型,如Ljava/lang/Object |
对于数组类型,每一唯独将使用一个前置的“[”字符来描述,如一个定义为
java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String]],一个整形数组int[]将被记录为:[I。对于描述方法,按照“先参数列表,后返回值的顺序描述”,参数列表按照参数的严格顺序放在一组小括号“()”内。例如:
void inc()的描述符为:()V。方法java.lang.String.toString()的描述符为:()Ljava/lang/String。方法
int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)的描述符为:([CII[CIII)I。
7. 方法表集合
在class文件中,所有的方法以二维表的形式存储,每张表来表示一个函数,一个类中的所有方法构成方法表的集合。
方法表的结构和字段表的结构一致,只不过访问标志和属性表集合的可选项有所不同。
| 类型 | 名称 | 数量 |
|---|---|---|
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
方法中的Java代码,经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为“Code”的属性里面,属性表作为Class文件格式中最具拓展性的一种数据项目,在第8节中讲。
8. 属性表集合
在Class文件、字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。
属性表集合对于各个属性表的顺序不再做严格要求,只要不与已有属性名重复即可。任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。
为了正确解析Class文件,虚拟机与定义了一些虚拟机实现应当能识别的属性,对于这些属性,它的名称需要从常量池中引用一个CONSTANT_Utf9_info类型的常量来表示,而属性值的结构则是完全自定义的。
一个符合规则的属性表应该有如下结构:
| 类型 | 名称 | 数量 |
|---|---|---|
| u2 | attribute_name_index | 1 |
| u4 | attribute_length | 1 |
| u1 | info | attribute_length |
JVM虚拟机 - Class类文件结构的更多相关文章
- 深入理解Java虚拟机(类文件结构)
深入理解Java虚拟机(类文件结构) 欢迎关注微信公众号:BaronTalk,获取更多精彩好文! 之前在阅读 ASM 文档时,对于已编译类的结构.方法描述符.访问标志.ACC_PUBLIC.ACC_P ...
- 《深入理解Java虚拟机》类文件结构
上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...
- 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)
目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...
- jvm(4):类文件结构
typora-root-url: ./ 类文件结构 魔数Magic Number 每个Class文件的头4个字节是魔数.值为0xCAFEBABE 唯一作用:确定这个文件是一个能被虚拟机接受的Class ...
- JVM笔记9-Class类文件结构
1.Class类文件结构 Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...
- Java虚拟机 - Class类文件结构
[深入Java虚拟机]之二:Class类文件结构 平台无关性 Java是与平台无关的语言,这得益于Java源代码编译后生成的存储字节码的文件,即Class文件,以及Java虚拟机的实现.不仅使用Jav ...
- Java虚拟机,类文件结构深度解析
Java类文件结构 Java虚拟机不和包括Java在内的任何语言绑定,只与 "Class文件" 这种特定的二进制文件所关联, Class文件中包含了Java虚拟机指令集合符号表以及 ...
- 深入理解Java虚拟机(七)——类文件结构
Java的无关性 由于计算机领域中有很多操作系统和硬件平台同时在竞争,所以,很多编程语言的程序设计会与其运行的平台和操作系统产生耦合,这样就大大增加了程序员的工作,为了适应不同的平台,需要修改很多代码 ...
- 【JVM.5】类文件结构
鲁迅曾经说过:代码编译的结构从本地机器码转变为字节码,是存储格式发展的一小步,确是编程语言发展的一大步. 一.无关性的基石 Java设计者在最初就承诺过“In the future, we will ...
随机推荐
- Installshield build all installer in development computer
Step: Copy all "SetupPrerequisites" from build server. please make sure below items: Insta ...
- Learning Python 011 高级特性 2
Python 高级特性 2 列表生成式 列表生成式就是指类似这样的代码:[x for x in range(1, 11)] >>> L = [x for x in range(1, ...
- 关于export环境变量生存期
今天对export方式设置的环境变量的生存期有很大疑惑. 按变量的生存周期来划分,Linux变量可分为两类,它们的修改方法如下:(1)永久的:需要修改配置文件,变量永久生效. 常见的配置文件包括 ...
- 除了BAT,计算机、软件专业的毕业生还有别的好去处吗?
"学技术的同学应该关注36氪.pingwest,极客公园这些圈子里很有影响力的科技媒体" 转载--除了BAT,计算机.软件专业的毕业生还有别的好去处吗? 又到校招季,985理工科的 ...
- Java Script 学习笔记 -- Ajax
AJAX 一 AJAX预备知识:json进阶 1.1 什么是JSON? JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.JSON是用字符串来表示Javas ...
- 【mysql格式化日期】
date_format(now(),'%Y-%c-%d'): 1. DATE_FORMAT() 函数用于以不同的格式显示日期/时间数据. DATE_FORMAT(date,format) format ...
- 项目接入apm后错误报警总结
此文已由作者张磊授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言: 后端服务一般都有监控措施,一般可以及时发现线上错误,但是很多项目的前端却没有线上报警服务,即使有错误, ...
- 用SQL数据库做多表关联应怎样设计库结构20170527
http://77857.blog.51cto.com/67857/143872/ 多表关联的话表之间必须得存在关系才行呢,这样建立外键约束就行了, 关系表中插入主表的主键做外键. 假设表1学生表st ...
- 2018牛客多校第九场E(动态规划,思维,取模)
#include<bits/stdc++.h>using namespace std;const long long mod=1000000007,inv=570000004;long l ...
- Spark 性能调优零散知识
1. 如果 Spark 中 CPU 的使用率不够高,可以考虑为当前的程序分配更多的 Executor, 或者增加更多的 Worker 实例来充分的使用多核的潜能 2. 适当设置 Partition 分 ...