Class文件由顺序的8位字节为基础单位构成的二进制流。各个项目严格按照顺序紧凑排列,无分隔符。

需要用8位字节以上空间数据项时按照高位在前分割成若干个8位字节存储。

只包含2种数据类型:

  • 无符号数

无符号数

是class文件基本数据类型,以u1,u2,u4,u8分别代表1个字节,2个,4个,8个字节的无符号数。可以用来描述数字、索引引用、数量值、以UTF-8编码构成的字符串。

由多个无符号数或其他表作为数据项构成的复合数据类型。所有表以“_info”结尾。用来描述具有层次关系的复合结构数据。

其实class文件本质上就是一张表。

Class文件结构

包含magic魔数,文件版本,常量池,访问标识,类索引、父类索引与接口索引集合,字段表集合,方法表集合,属性表集合等构成

魔数

在class文件头4个字节,magic number。

作用:确定该文件是否是虚拟机可接受的class文件。java的魔数统一为 0xCAFEBABE (来源于一款咖啡)。

版本号

由minorversion和majorversion组成,占用文件中第5到8字节。

CA FE BA BE 00 00 00 32

表示此版本号为50.0,十进制表示一般格式为 major.minor

需要注意的是java版本号是从45开始的,大版本发布,主版本号+1.高版本的jdk能向下兼容以前版本的class文件,但不兼容以后版本的class文件。

各jdk编译后的class文件版本号如下:


常量池

紧接着版本号后就是常量池入口。是第一个出现的表类型的数据项目。

由常量池容量计数值constant_pool_count及常量池constant_pool组成。

constant_pool_count占2个字节。如 0×0016 即十进制的22,表示有21项常量,从1开始到21.

0有特殊含义,表示指向常量池的索引值数据不引用任何一个常量池项目。

常量池主要存放字面量Literal和符号引用Symbolic References。

字面量可能是文本字符串,或final的常量值。

符号引用包括以下:

  • 类或接口全限定名 Full Qualified Name
  • 字段名称和描述符 Descriptor
  • 方法名称和描述符

常量池共有11种表结构数据。

每个表均开始的第一位是u1类型的标志位,表示哪种常量类型。但每个表的结构都不相同。

如类或接口的符号引用为CONSTANTClassinfo.

所有的常量池结构严格以标志位,表结构其他数据类型 顺序排列。

各类型常量池表结构如下表所示:

实际中可以使用 javap -verbose XXX.class 输出字节码内容。

访问标志access_flags

u2类型,识别类或接口层次的访问信息,如class是接口或类,是否public,是否abstract,是否final等。


类索引、父类索引与接口索引集合

类索引this_class及父类索引super_class均是u2类型,接口索引集合interfaces是一组u2类型的集合。确定类的继承关系.按照顺序排列在访问标志之后。this_class,super_class指向CONSTANT_CLASS_info常量。通过该常量可以找到定义在CONSTANT_Utf8_info的全限定名字符串。

字段表集合field_info

紧接在interface后面的是fieldscount的计数,它是类变量和实例变量的字段的数量总和。在这个计数后面的是不同长度的fieldinfo表的序列(fieldscount指出了序列中有多少个fieldinfo表)。

描述接口或类中声明的变量,包含类级变量或实例级变量,不包含方法变量。信息包含:字段作用域、字段类型(类变量还是实例变量 static)、可变性(final)、并发可见性(volatile)、可否序列化(transient)、字段数据类型、名称等。

结构如下表:

字段修饰符放在accessflags项目中,它与类中的accessflags项目是非常相似的,都是一个u2的数据类型,其中可以设置 的标志位和含义如下表所示

nameindex和descriptorindex都是对常量池的引用,分别代表着字段的简单名称及字段和方法的描述符.其中描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。

对于数组类型,每一个维度将使用一个前置的”["字符来描述,如一个定义的"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[CII)I.

在后面是attributes_count和attributes,分别表示属性计数器和属性信息,用来表示字段的额外属性,如果属性计数器为0,表示无额外属性。如

final static int m=500;

此时attributes_count为1,表示有1个属性,属性ConstantValue指向常量池中得常量500.

字段表集合不会列出从超类或父接口中继承来的字段,但可能包含原代码中不存在的字段,如内部类中为了保持对外部类的访问性,会自动添加指向外部类的字段。

java中类变量或实例变量不能重名,但在字节码中,只要描述符不一致,字段重名也是合法的。

方法表集合methods_info

紧接着field后面的是对在该类或者接口中所声明的方法的描述。其结构与fields一样,不一样的是访问标志。

访问标志中没有了ACCVOLATILE和ACCTRANSIENT标志。增加了ACCSYNCHRONIZED,ACCNATIVE,ACCSTRICTFP,ACCABSTRAT.

附:strictfp(strict float point (精确浮点))用法

strictfp 关键字可应用于类、接口或方法。使用 strictfp 关键字声明一个方法时,该方法中所有的float和double表达式都严格遵守FP-strict的限制,符合IEEE-754规范。当对一个类或接口使用 strictfp 关键字时,该类中的所有代码,包括嵌套类型中的初始设定值和代码,都将严格地进行计算。严格约束意味着所有表达式的结果都必须是
IEEE 754 算法对操作数预期的结果,以单精度和双精度格式表示。

方法里的代码经过编译器编译成字节码指令之后,存放在方法属性表集合中Code得属性里面。

另外如果父类方法在子类中没有被重写(Override),方法表集合就不会出现来自父类的方法信息。同时,也可能出现编译器自动添加的方法,如类构造器和实例构造器方法。

java中方法都有一个特征签名,指的就是一个方法中各参数在常量池中字段符号引用的集合。这里并不包含返回值,所以java中重载不能仅仅依靠返回值不同。但对于class文件,只要描述符不同的方法就可以共存在一个class文件中,所以两个方法有相同的名称和特征签名,但返回值不同,在class文件中可以共存。

属性表集合attribute_info

class文件中最后的部分是属性,它给出了在该文件类或者接口所定义的属性的基本信息。属性部分由attributescount开始,attributescount是指出现在后续attributes列表的attributeinfo表的数量总和。每个attributeinfo的第一项是指向常量池中CONSTANTUtf8info表的引用,该表给出了属性的名称。

属性表用来表示专用场景的专有信息,比如前面的字段表、方法表都有自己的属性信息。这里不要求严格的顺序,只要不与已有属性名重复,任何人实现的编译器都可以向属性表写入自定义的属性信息。jvm运行时会忽略掉不认别的属性。

《Java虚拟机规范(第2版)》预定义了9项属性。

每个属性的名称需要从常量池中引用一个CONSTANTUtf8info类型的常量表示。属性值结构完全自定义,只要说明属性值占用的位数长度即可。

类型 名称 数量
u2 attributenameindex 1
u2 attribute_length 1
u1 info attribute_length

Code属性

其中max_stack表示操作数栈(operand stacks)深度最大值,jvm根据该值分配栈帧Frame中操作栈深度。

max_locals表示局部变量表所需存储空间。单位是Slot,虚拟机为局部变量分配内存的最小单位。对byte、char,float,int,short,boolean,reference,returnAddress等长度不超过32位的数据类型,占用1Slot.而double,long类型需要2Slot。

方法中的局部变量表存放的包括实例方法隐藏参数this,显示异常处理的参数(ExceptionHandler Parameter),方法体中用的局部变量等。同时局部变量表中得Slot可以重用,编译器根据变量作用域来分类Slot,并分配给各变量使用,然后计算max_locals。

code和code_length存储字节码指令。每个指令为u1类型的单字节,取值范围为0×00~0xFF,十进制的0~255.既可以表达256个指令。目前jvm定义了约200条编码值对应的指令含义。部分指令码含义摘录如下:

另外code_length是u4类型的长度值,理论上最大值可以是232-1,但jvm规范规定一个方法不允许超过65535条,否则编译器会拒绝编译。

深入JVM的Class文件结构的更多相关文章

  1. 深入理解JVM(六)类文件结构

    6.1 关于类文件 1.class文件的一次编译,到处运行的跨平台性: 2.JVM不止有跨平台性,还有跨语言性,不管是JRuby还是Groovy写出来的程序,只要编译出符合JVM规范的class文件就 ...

  2. JVM(4) 类文件结构

    一.实现“平台无关性” 字节码(ByteCode)存储格式和虚拟机是实现语言无关性的基础.Java虚拟机不和包括Java在内的任何语言绑定,它只与“Clas”文件这种特定的二进制文件格式所关联,Cla ...

  3. JVM之Class文件结构

    每一个class文件对应一个类或者接口,但是一个类或者接口不一定生成class文件,classloader直接生成. 8为字节为基础的二进制流,各个数据项按照严格的顺序排列在class文件中,没有任何 ...

  4. JVM初探之类文件结构

    java 的 .class 文件字节码如图:Class 文件是一组以8字节为基础单位的二进制流,各个数据项目有着严格的结构.下面我们介绍下class文件的结构. 魔数 class文件的头4个字节是魔数 ...

  5. JVM小结--类文件结构

    字节码是构成Java平台无关性的基石.实现语言无关性的基础是虚拟机和字节码存储格式. Java语言中的各种变量.关键字和运算符的语义最终是由多条字节码命令组成,因此字节码命令所能提供的语义描述能力肯定 ...

  6. 【JVM】虚拟机类加载机制

    什么是类加载 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. <[JVM]类文件结构& ...

  7. Java Class文件详解

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) Java Class文件中包含以下信息: [+]view code ClassFile { u4 magic;  ...

  8. 我的阿里之路+Java面经考点

    我的阿里之路+Java面经考点 时间:2018-03-19 23:03  来源:未知   作者:admin   点击:87次 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三 ...

  9. JVM基础系列第5讲:字节码文件结构

    温馨提示:此篇文章长达两万字,图片50多张,内容非常多,建议收藏后再看. 前面我们说到 Java 虚拟机使用字节码实现了跨平台的愿景,无论什么系统,我们都可以使用 Java 虚拟机解释执行字节码文件. ...

  10. JVM学习04:类的文件结构

    JVM学习04:类的文件结构 写在前面:本系列分享主要参考资料是  周志明老师的<深入理解Java虚拟机>第二版. 类的文件结构知识要点Xmind梳理

随机推荐

  1. Java并发之原子变量及CAS算法-下篇

    Java并发之原子变量及CAS算法-下篇 概述 本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK提供的类中是怎么保证变量原子性的呢?.对应Java中的包是:java.util.con ...

  2. 【YashanDB数据库】VMware虚拟机使用默认安装,在掉电之后数据库无法启动

    问题现象 客户使用VMware虚拟机使用默认安装,部署YashanDB个人版,在掉电之后无法启动 操作系统: Virtualization: VMware Operating System: Cent ...

  3. RxJS 系列 – Filtering Operators

    前言 介绍完 RxJS 两大概念 Observable 和 Subject 之后, 篇幅最大的就是各种 Operators 了. 这篇先介绍比较简单的 Filter Operators. 请先看完上面 ...

  4. CTFSHOW pwn03 WrriteUp

    本文来自一个初学CTF的小白,如有任何问题请大佬们指教! 题目来源 CTFShow pwn - pwn03 (ret2libc) https://ctf.show/challenges 思路 1.下载 ...

  5. 10-04 NOIP模拟赛

    10-04 NOIP模拟赛 喜提80+30+10=120分 T1 水管(flow) 题目描述 某国有 \(n\) 个城市,水利调配非常有问题,这导致部分地区出现严重的缺水,而部分地区却全年洪灾泛滥.政 ...

  6. 从0到1搭建权限管理系统系列四 .net8 中Autofac的使用(附源码)

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  7. LeetCode 1316. Distinct Echo Substrings (RK哈希)

    题意: 给一个字符串 寻找字符串为(a+a)格式的子串有多少.a+a 格式字符串比如 abcabc, ee 等. 首先O(N^2)枚举子串,然后通过哈希在O(1)复杂度判断子串是否符合要求. RK哈希 ...

  8. foobar2000 v2.1.5 汉化版(更新日期:2024.09.08)

    foobar2000 v2.1.5 汉化版 -----------------------[软件截图]---------------------- -----------------------[软件 ...

  9. 使用dynamic debug帮助调试

    你一定在kernel source code中看过很多pr_debug()/dev_dbg()/print_hex_dump_debug()吧,这些debug语句提供更多的信息帮助我们了解内核运行流程 ...

  10. Ftrace 进阶用法

    1 前言 本文为 Ftrace 系列文章第二篇,描述 Ftrace 进阶用法.上一篇文章中我们接触到了 Ftrace 基本概念,知道了如何 trace 一个函数,知道了如何 enable 一个 tra ...