Class文件
- 源代码经过编译器编译之后便会生成一个字节码文件
- 字节码是一种二进制的类文件,它的内容是JVM的指令
- 不是C、C++经由编译器直接生成机器码
概述
字节码文件的跨平台性
Java语言跨平台语言
- Java源代码编译为字节码文件在不同平台上运行,不需要重新编译
- 其他语言有编译器,跨平台优势不明显了
- 跨平台似乎快成了一门语言必有的特性
跨语言的平台——JVM
- Java虚拟机不和包括Java在内的任何语言绑定
- 它只与Class文件,特定的二进制文件格式所关联
- 只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行
- Class文件结构,Java虚拟机的基石、桥梁
- 所有的JVM遵循JVM规范,字节码文件可以在所有的JVM运行
- 前端编译器负责将Java源码编译为JVM规范的字节码文件
- Javac是一种前端编译器,过程有:
- 词法分析
- 语法解析
- 语义解析
- 生成字节码
- JDK包含将源码编译为JVM指令集的编译器、JVM运行时环境
编译器
Javac编译器
- javac编译器能将Java源代码编译为字节码(前端编译器)
- HotSpot VM并没有强制要求前端编译器只能使用javac来编译字节码,其实只要编译结果符合JVM规范都可以被JVM所识别即可
ECJ编译器
- 内置在Eclipse中的ECJ (Eclipse Compiler for Java)编译器
- 和Javac的全量式编译不同,ECJ是一种增量式编译器
- ECJ编译器所采取的编译方案是把未编译部分的源码逐行进行编译,而非每次都全量编译
- Tomcat中同样也是使用ECJ编译器来编译isp文件
- ECJ编译器是采用GPLv2的开源协议进行源代码公开,可以登录eclipse官网下载ECJ编译器的源码进行二次开发
- 前端编译器并不会直接涉及编译优化等方面的技术,而是将这些具体优化细节移交给HotSpot的JIT编译器负责
AOT(静态提前编译器)
字节码指令
- Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的操作码(opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(operand)所构成
- 虚拟机中许多指令并不包含操作数(只有一个操作码)
文件结构
类(Class)
- 任何一个Class文件都对应着唯一一个类或接口的定义信息
- Class文件实际上它并不一定以磁盘文件的形式存在
- Class文件是一组以8位字节为基础单位的二进制流
文件格式
- Class的结构不像XML等描述语言,由于它没有任何分隔符号
- 其中的数据项,无论是字节顺序还是数量,都是被严格限定的,哪个字节代表什么含义、长度是多少、先后顺序如何、都不允许改变
- Class文件的结构并不是一成不变的,随着Java虚拟机的不断发展,总是不可避免地会对Class文件结构做出一些调整,但是其基本结构和框架是非常稳定的
总体结构
类型 | 名称 | 说明 | 长度 | 数量 |
---|---|---|---|---|
u4 | magic | 魔数,识别Class文件格式 | 4个字节 | 1 |
u2 | minor_version | 副版本号(小版本) | 2个字节 | 1 |
u2 | major_version | 主版本号(大版本) | 2个字节 | 1 |
u2 | constant_pool_count | 常量池计数器 | 2个字节 | 1 |
cp_info | constant_pool | 常量池表 | n个字节 | constant_pool_count-1 |
u2 | access_flags | 访问标识 | 2个字节 | 1 |
u2 | this_class | 类索引 | 2个字节 | 1 |
u2 | super_class | 父类索引 | 2个字节 | 1 |
u2 | interfaces_count | 接口计数器 | 2个字节 | 1 |
u2 | interfaces | 接口索引集合 | 2个字节 | interfaces_count |
u2 | fields_count | 字段计数器 | 2个字节 | 1 |
field_info | fields | 字段表 | n个字节 | fields_count |
u2 | methods_count | 方法计数器 | 2个字节 | 1 |
method_info | methods | 方法表 | n个字节 | methods_count |
u2 | attributes_count | 属性计数器 | 2个字节 | 1 |
attribute_info | attributes | 属性表 | n个字节 | attributes_count |