Java编译期的概念
编译期即将源代码转变成本地代码的过程,对于Java来说,这个过程会更加的多样。
Java编译期因为采用的编译器类型不同而导致概念也不同,Java编译器可以分为以下三种类型:
前端编译器
前端编译器将java源代码转变成字节码,常见的前端编译器包括Javac编译器、ECJ编译器(Eclipse JDT)。
即时(JIT,Just In Time)编译器
即时编译器将字节码转变成本地机器码,常见的即时编译器包括HotSpot 虚拟机的C1、C2编译器。
AOT(Ahead of Time)编译器
AOT编译器直接将java源代码变成本地机器码,常见的AOT编译器包括GCJ编译器(GNU Compiler for the Java)
本文主要阐述Java前端编译器的工作流程。
Javac 编译器的编译过程
Javac编译器本身也是采用Java语言实现的一款前端编译器,Javac编译的流程如下图所示:
解析与填充符号表
这个过程主要包含两个部分:词法语法分析和填充符号表
词法分析即将java源代码转换成token,语法分析再根据token构造出抽象语法树(AST,Abstract Syntax Tree)。然后根据词法语法分析之后的结果填充符号表。插入式注解处理
JDK 1.6开始提供了一组插入式的注解处理器API,可以实现在编译期间对注解进行处理,通过实现插入式注解处理器可以访问和修改抽象语法树中的任意元素。如果一个插入式注解处理器修改了抽象语法树,则在修改语法树之后回到解析与填充符号表的过程重新处理。
分析与生成字节码
这个过程主要对生成的抽象语法树进行语义分析,而后生成所需的字节码。语义分析具体分可以分为四个步骤:
- 标注检查
标注检查包括:变量使用前是否声明、变量类型是否匹配等。
标注检查还包括一个重要的优化行为——常量折叠:
int a = 1 + 2;
会被优化成int a = 3;
- 数据与控制流分析
数据与控制流分析是用于检查程序的上下文逻辑是否正确,包括:方法是否每条执行路径都有返回值、异常是否被抛出或者被catch等。 - 解语法糖
Java提供了很多语法糖,常见的如:泛型、自动装箱拆箱、变长参数、for-each循环、断言语句等。这些语法糖在编译期都会被还原成简单的基础语法。 - 字节码生成
字节码生成即将前面几个步骤生成的信息转换成字节码生成Class文件。
在这个过程中,编译器会自动生成<init>
方法和<clinit>
方法插入语法树。
编译器将实例变量的初始化、语句块、调用父类实例构造器等操作整理生成类的实例构造方法<init>
。将static变量的初始化、static语句块等操作整理生成类的构造方法<clinit>
。
- 标注检查
参考资料:《深入理解Java虚拟机》