目录
- Java程序编译
- class文件内容
- 常量池
- 附录-访问标识表
- 附录-常量池类型列表
Java程序编译
Java文件通过编译成class文件后,通过JVM虚拟机解释字节码文件转为操作系统执行的二进制码运行。
规范
Java虚拟机有自己的一套规范,遵循这套规范,任何形式的语言都可以在JVM上运行,前提是编译成class文件,此规范相见oracle官网文档。
实现
JVM虚拟机落地实现有很多,常见的有:
- Hotspot,使用最热门的JVM
- OpenJ9,IBM开发
- MicroSoft JVM 微软开发
- TaobaoVM 淘宝开发
- azul zing VM 垃圾回收行业标杆,最快
class文件内容
从上图内容可看出class文件包以下几个方面内容:
- 魔数(
magic number
)占4个字节,固定的值0xcafebabe
,主要来校验class文件。 - version 版本号,分为小版本号
minor version
和大版本号major version
,各占两个字节。 - 常量池信息(
constant_pool
),具体的各种常量信息,字符串,类名,方法名,字段名等。 - 访问标识(
access_flag
),2个字节描述类与接口的修饰符,有几个常见的常量值见附录-访问标识 - 当前类(
this_class
) - 当前类的父类(
super_class
) - 具体的接口信息(
interfaces
) - 具体的属性信息(
fileds
) - 具体的方法信息(
methods
) - 其它附加属性信息(
attributes
)
更具体的内容如下:
项目 | Value |
---|---|
Magic Number | 魔数(CAFE BABE) |
Minor Number | 次版本号 |
Major Number | 主版本号 |
constant_pool_count | 常量池数量 |
constant_pool | 常量池具体实现,是一种表 |
access_flag | 访问标识 |
this_class | 当前类名 |
super_class | 父类名称 |
interfaces_count | 接口数量 |
interfaces | 具体实现的接口信息 |
fileds_count | 属性数量 |
fileds | 具体属性信息 |
methods_count | 方法数量 |
methods | 具体方法的信息 |
attributes_count | 其它附加属性的数量 |
attributes | 其它附加属性具体信息 |
现在来看具体示例来看上面的代表是什么意思,准备一个简单的类:
public class ByteCode01 {
public ByteCode01() {
}
}
编译之后的字节码文件:
class文件中就是一个二进制字节流,非0即1,数据类型有五种,u1,u2,u4,u8和_info表类型
。
上述这种文件需要借助工具来分析具体内容,在idea安装jclasslib
插件看Java文件编译成class文件信息,选中编译后的class文件,如下图所示:
访问标识是固定的值,通过位运算得出的0x0021
既代表public
又代表super
(0x0021
是0x0001
与0x0020
位与运算),详见附录-访问标识表
更直观的表现:
常量池
本类索引即当前类名(this_class
),cp_info
是constant_pool
的表类型,#2
是第二张表,cp_info #2
代表常量池第二张表,存的是对cp_info #14
的引用,cp_info #14
存的是具体的字符串值,就是当前类的类名。
class文件中都有属于自己的常量池,常量池包含类型列表,来存储类、方法、字段、接口等信息,以及各种字面量,通过解析常量池可以访问这些字符串信息。比如上图出现的CONSTANT_Utf8_info
、CONSTANT_Class_info
等详见文末常量池类型列表。
着重看下CONSTANT_Utf8_info
,它用来存储 UTF-8 编码的字符串,相当于一张常量池表,看一下它的具体结构:
字段名 | 字节数 | 描述 |
---|---|---|
tag | 1 | CONSTANT_Utf8_info的常量类型是1(其余的见文末常量池类型列表) |
length | 2 | u2(2 字节无符号数) 类型字段,表示 bytes 部分字节长度,以字节为单位的 |
bytes | length | 实际字符串数据,采用 UTF-8 编码显示 |
比如
public class Test {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
字符串 Hello, World!
,会以 CONSTANT_Utf8_info
类型存储在常量池中,对应的二进制内容或许是:
tag = 0x01 (标识 CONSTANT_Utf8_info)
length = 0x000D (字符串的字节长度为 13)
bytes = 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 ("Hello, World!" 的 UTF-8 编码)
常量池通常分为下面几种常量值类型:
类型 | 标志(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 | 表示一个动态方法调用点 |
附录-访问标识表
项目 | Value |
---|---|
ACC_PUBLIC 0x0001 | 是否是public类型 |
ACC_FINAL 0x0010 | 是否是NULL |
ACC_SUPER 0x0020 | 该标志必须为真 |
ACC_INTERFACE 0x0200 | 是否为接口 |
ACC_ABSTRACT 0X0400 | 是否是接口或者抽象类 |
ACC_SYNTHETIC 0x1000 | 编译器自动生成,非用户代码产生 |
ACC_ANNOTATION 0x2000 | 是否为注解 |
ACC_ENUM 0x4000 | 是否为枚举 |
附录-常量池类型列表
常量类型 | tag值 | 描述 |
---|---|---|
CONSTANT_Utf8_info | 1 | 用于存储 UTF-8 编码的字符串 |
CONSTANT_Integer_info | 3 | 用于存储 4 字节的整型字面量(int ) |
CONSTANT_Float_info | 4 | 用于存储 4 字节的浮点型字面量(float ) |
CONSTANT_Long_info | 5 | 用于存储 8 字节的长整型字面量(long ) |
CONSTANT_Double_info | 6 | 用于存储 8 字节的双精度浮点型字面量(double ) |
CONSTANT_Class_info | 7 | 用于存储类或接口的符号引用,引用 CONSTANT_Utf8_info 中的类名 |
CONSTANT_String_info | 8 | 用于存储字符串字面量,引用 CONSTANT_Utf8_info 中的字符串内容 |
CONSTANT_Fieldref_info | 9 | 用于存储字段的符号引用,引用类和字段的描述信息 |
CONSTANT_Methodref_info | 10 | 用于存储类中方法的符号引用,引用类和方法的描述信息 |
CONSTANT_InterfaceMethodref_info | 11 | 用于存储接口中方法的符号引用,引用接口和方法的描述信息 |
CONSTANT_NameAndType_info | 12 | 用于描述字段或方法的名称和类型,引用名称(Utf8 )和描述符(Utf8 ) |
CONSTANT_MethodHandle_info | 15 | 用于存储对方法句柄的引用(invokedynamic 指令用) |
CONSTANT_MethodType_info | 16 | 用于存储方法类型的符号引用,指向方法描述符(Utf8 ) |
CONSTANT_Dynamic_info | 17 | 用于存储动态调用点的信息(JDK 11 引入,支持动态常量) |
CONSTANT_InvokeDynamic_info | 18 | 用于存储动态方法调用点的信息,引用引导方法和动态调用名称 |
CONSTANT_Module_info | 19 | 用于存储模块的符号引用(JDK 9 引入) |
CONSTANT_Package_info | 20 | 用于存储包的符号引用(JDK 9 引入) |