共计 3051 个字符,预计需要花费 8 分钟才能阅读完成。
类文件构造
JVM 的“无关性”
议论 JVM 的无关性,次要有以下两个:
- 平台无关性:任何操作系统都能运行 Java 代码
- 语言无关性:JVM 能运行除 Java 以外的其余代码
Java 源代码首先须要应用 Javac 编译器编译成 .class 文件,而后由 JVM 执行 .class 文件,从而程序开始运行。
JVM 只意识 .class 文件,它不关怀是何种语言生成了 .class 文件,只有 .class 文件合乎 JVM 的标准就能运行。目前曾经有 JRuby、Jython、Scala 等语言可能在 JVM 上运行。它们有各自的语法规定,不过它们的编译器 都能将各自的源码编译成合乎 JVM 标准的 .class 文件,从而可能借助 JVM 运行它们。
Java 语言中的各种变量、关键字和运算符号的语义最终都是由多条字节码命令组合而成的,因而字节码命令所能提供的语义形容能力必定会比 Java 语言自身更加弱小。因而,有一些 Java 语言自身无奈无效反对的语言个性,不代表字节码自身无奈无效反对。
Class 文件构造
Class 文件是二进制文件,它的内容具备严格的标准,文件中没有任何空格,全都是间断的 0/1。Class 文件 中的所有内容被分为两种类型:无符号数、表。
- 无符号数 无符号数示意 Class 文件中的值,这些值没有任何类型,但有不同的长度。u1、u2、u4、u8 别离代表 1/2/4/8 字节的无符号数。
- 表 由多个无符号数或者其余表作为数据项形成的复合数据类型。
Class 文件具体由以下几个形成:
- 魔数
- 版本信息
- 常量池
- 拜访标记
- 类索引、父类索引、接口索引汇合
- 字段表汇合
- 办法表汇合
- 属性表汇合
魔数
Class 文件的头 4 个字节称为魔数,用来示意这个 Class 文件的类型。
Class 文件的魔数是用 16 进制示意的“CAFE BABE”,是不是很具备浪漫色调?
魔数相当于文件后缀名,只不过后缀名容易被批改,不平安,因而在 Class 文件中标识文件类型比拟适合。
版本信息
紧接着魔数的 4 个字节是版本信息,5-6 字节示意次版本号,7-8 字节示意主版本号,它们示意以后 Class 文件中应用的是哪个版本的 JDK。
高版本的 JDK 能向下兼容以前版本的 Class 文件,但不能运行当前版本的 Class 文件,即便文件格式并未产生任何变动,虚拟机也必须拒绝执行超过其版本号的 Class 文件。
常量池
版本信息之后就是常量池,常量池中寄存两种类型的常量:
字面值常量
字面值常量就是咱们在程序中定义的字符串、被 final 润饰的值。
符号援用
符号援用就是咱们定义的各种名字:类和接口的全限定名、字段的名字和描述符、办法的名字和描述符。
常量池的特点
- 常量池中常量数量不固定,因而常量池结尾搁置一个 u2 类型的无符号数,用来存储以后常量池的容量。
- 常量池的每一项常量都是一个表,表开始的第一位是一个 u1 类型的标记位(tag),代表以后这个常量属于哪种常量类型。
常量池中常量类型
类型 | tag | 形容 |
---|---|---|
CONSTANT_utf8_info | 1 | UTF-8 编码的字符串 |
CONSTANT_Integer_info | 3 | 整型字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号援用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号援用 |
CONSTANT_Methodref_info | 10 | 类中办法的符号援用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中办法的符号援用 |
CONSTANT_NameAndType_info | 12 | 字段或办法的符号援用 |
CONSTANT_MethodHandle_info | 15 | 示意办法句柄 |
CONSTANT_MethodType_info | 16 | 标识办法类型 |
CONSTANT_InvokeDynamic_info | 18 | 示意一个动静办法调用点 |
对于 CONSTANT_Class_info(此类型的常量代表一个类或者接口的符号援用),它的二维表构造如下:
类型 | 名称 | 数量 |
---|---|---|
u1 | tag | 1 |
u2 | name_index | 1 |
tag 是标记位,用于辨别常量类型;name_index 是一个索引值,它指向常量池中一个 CONSTANT_Utf8_info 类型常量,此常量代表这个类(或接口)的全限定名,这里 name_index 值若为 0x0002,也即是指向了常量池中的第二项常量。
CONSTANT_Utf8_info 型常量的构造如下:
类型 | 名称 | 数量 |
---|---|---|
u1 | tag | 1 |
u2 | length | 1 |
u1 | bytes | length |
tag 是以后常量的类型;length 示意这个字符串的长度;bytes 是这个字符串的内容(采纳缩略的 UTF8 编码)
拜访标记
在常量池完结之后,紧接着的两个字节代表拜访标记,这个标记用于辨认一些类或者接口档次的访问信息,包含:这个 Class 是类还是接口;是否定义为 public 类型;是否被 abstract/final 润饰。
类索引、父类索引、接口索引汇合
类索引和父类索引都是一个 u2 类型的数据,而接口索引汇合是一组 u2 类型的数据的汇合,Class 文件中由这三项数据来确定类的继承关系。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。
因为 Java 不容许多重继承,所以父类索引只有一个,除了 java.lang.Object 之外,所有的 Java 类都有父类,因而除了 java.lang.Object 外,所有 Java 类的父类索引都不为 0。一个类可能实现了多个接口,因而用接口索引汇合来形容。这个汇合第一项为 u2 类型的数据,示意索引表的容量,接下来就是接口的名字索引。
类索引和父类索引用两个 u2 类型的索引值示意,它们各自指向一个类型为 CONSTANT_Class_info 的类描述符常量,通过该常量总的索引值能够找到定义在 CONSTANT_Utf8_info 类型的常量中的全限定名字符串。
字段表汇合
字段表汇合存储本类波及到的成员变量,包含实例变量和类变量,但不包含办法中的局部变量。
每一个字段表只示意一个成员变量,本类中的所有成员变量形成了字段表汇合。字段表构造如下:
类型 | 名称 | 数量 | 阐明 |
---|---|---|---|
u2 | access_flags | 1 | 字段的拜访标记,与类稍有不同 |
u2 | name_index | 1 | 字段名字的索引 |
u2 | descriptor_index | 1 | 描述符,用于形容字段的数据类型。根本数据类型用大写字母示意;对象类型用“L 对象类型的全限定名”示意。 |
u2 | attributes_count | 1 | 属性表汇合的长度 |
u2 | attributes | attributes_count | 属性表汇合,用于寄存属性的额定信息,如属性的值。 |
字段表汇合中不会呈现从父类(或接口)中继承而来的字段,但有可能呈现本来 Java 代码中不存在的字段,譬如在内部类中为了放弃对外部类的拜访性,会主动增加指向外部类实例的字段。
办法表汇合
办法表构造与属性表相似。
volatile 关键字 和 transient 关键字不能润饰办法,所以办法表的拜访标记中没有 ACC_VOLATILE 和 ACC_TRANSIENT 标记。
办法表的属性表汇合中有一张 Code 属性表,用于存储以后办法经编译器编译后的字节码指令。
属性表汇合
每个属性对应一张属性表,属性表的构造如下:
类型 | 名称 | 数量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u1 | info | attribute_length |
本文由 mdnice 多平台公布