概述

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

Class文件格式中只有两种数据类型:无符号数

  • 无符号数属于最基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,以“_info”结尾。表用来描述有层次关系的符合结构的数据。

整个Class文件本质上就是一张表。

无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,称这一系列连续的某一类型的数据位某一类型的集合。

class文件的组织结构

  1. 魔数
  2. 本文件的版本信息
  3. 常量池
  4. 访问标志
  5. 类索引
  6. 父类索引
  7. 接口索引集合
  8. 字段表集合
  9. 方法表集合
  10. 属性表集合

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. 常量池的特点

  1. 常量池长度不固定

    常量池的大小是不固定的,因此常量池开头放置一个u2类型的无符号数,用来存储当前常量池的容量。JVM根据这个值就知道常量池的头尾来。

    这个无符号数从1开始,不是通常的从0开始

  2. 常量池中的常量由二维表来表示

    常量池开头有个常量池容器计数器,接下来就全是一个个常量了,只不过常量都是由一张张二维表构成,除了记录常量的值以外,还记录当前常量的相关信息。

  3. Class文件之中的资源仓库

  4. Class文件结构中与其他项目关联最多的数据类型

  5. 占用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
  1. 对于数组类型,每一唯独将使用一个前置的“[”字符来描述,如一个定义为java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String]],一个整形数组int[]将被记录为:[I

  2. 对于描述方法,按照“先参数列表,后返回值的顺序描述”,参数列表按照参数的严格顺序放在一组小括号“()”内。例如: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类文件结构的更多相关文章

  1. 深入理解Java虚拟机(类文件结构)

    深入理解Java虚拟机(类文件结构) 欢迎关注微信公众号:BaronTalk,获取更多精彩好文! 之前在阅读 ASM 文档时,对于已编译类的结构.方法描述符.访问标志.ACC_PUBLIC.ACC_P ...

  2. 《深入理解Java虚拟机》类文件结构

    上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...

  3. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  4. jvm(4):类文件结构

    typora-root-url: ./ 类文件结构 魔数Magic Number 每个Class文件的头4个字节是魔数.值为0xCAFEBABE 唯一作用:确定这个文件是一个能被虚拟机接受的Class ...

  5. JVM笔记9-Class类文件结构

    1.Class类文件结构  Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个 Class 文件中 ...

  6. Java虚拟机 - Class类文件结构

    [深入Java虚拟机]之二:Class类文件结构 平台无关性 Java是与平台无关的语言,这得益于Java源代码编译后生成的存储字节码的文件,即Class文件,以及Java虚拟机的实现.不仅使用Jav ...

  7. Java虚拟机,类文件结构深度解析

    Java类文件结构 Java虚拟机不和包括Java在内的任何语言绑定,只与 "Class文件" 这种特定的二进制文件所关联, Class文件中包含了Java虚拟机指令集合符号表以及 ...

  8. 深入理解Java虚拟机(七)——类文件结构

    Java的无关性 由于计算机领域中有很多操作系统和硬件平台同时在竞争,所以,很多编程语言的程序设计会与其运行的平台和操作系统产生耦合,这样就大大增加了程序员的工作,为了适应不同的平台,需要修改很多代码 ...

  9. 【JVM.5】类文件结构

    鲁迅曾经说过:代码编译的结构从本地机器码转变为字节码,是存储格式发展的一小步,确是编程语言发展的一大步. 一.无关性的基石 Java设计者在最初就承诺过“In the future, we will ...

随机推荐

  1. CountDownLatch分析

    1 什么是CountDownLatch呢? 先看看官网的定义 :一种同步帮助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成. 现在由我来解释什么是CountDownLatch吧:比如说我 ...

  2. [HDU1711]KMP模板

    解题关键:1.直接套kmp模板即可,注意最后输出的位置,需要在索引的位置+1. 2.next用作数组名在oj中会编译错误, 3.选用g++,只有g++才会接受bits/stdc++.h OJ中g++和 ...

  3. [原创]SQL 表值函数:获取从今天计算起往前自定义天数

    PS:此博文是利用Windows Live Writer 2012编写,格式效果可能不太好. 在我开发过程中,遇到一个统计需求,结果是要求返回从当天起往回推算出自定义输入的天数 为此我写了一个表值函数 ...

  4. Spring IOP 没用

    Spring提供了很多轻量级应用开发实践的工具集合,这些工具集以接口.抽象类.或工具类的形式存在于Spring中.通过使用这些工具集,可以实现应用程序与各种开源技术及框架间的友好整合.比如有关jdbc ...

  5. 【jeasyui5】样式:调整页面显示的顶部菜单和左侧菜单

    1.顶部菜单修改:修改index2.js里面的InitTopMenu方法,将icon +2 2.左侧菜单宽度调整: 修改index.html,加上width:170的定长 <!-- 左侧菜单 - ...

  6. 大数据实习之spark

    Apache Spark是一个围绕速度.易用性和复杂分析构建的大数据处理框架. 与 Hadoop 和 Storm 等其他大数据和 MapReduce 技术相比,Spark 有如下优势. 首先,Spar ...

  7. 关于goneaway及499

    关于上面现象的分析如下 问题描述: 接口偶发性出现接口耗时过长的情况 根源: “sockets的快速回收”机制被启动 简单代码+数据分析: 1.      经简单分析,耗时主要出现在连接数据库的方法: ...

  8. 新一代web框架Koa源码学习

    此文已由作者张佃鹏授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Koa 就是一种简单好用的 Web 框架.它的特点是优雅.简洁.表达力强.自由度高.本身代码只有1000多行 ...

  9. SQL Server 2012 安装——安装 OR 卸载

    前言     上篇介绍了.net framework 3.5的安装,这次介绍一下,SQL Server2012的安装和彻底卸载.根据百度,下面是自己根据自己实际情况整理的安装步骤: 安装 见安装步骤( ...

  10. 如何使用JDBC Request跨数据库查询后引用查询的结果作为下一个JDBC Request的入参

    [前言] 今天来给大家介绍下如何使用JDBC Request跨数据库查询后引用查询的结果作为下一个JDBC Request的入参! 因为我现在所测的系统模块中部分表在不同的数据库中,所以在用JDBC ...