【笔记】前端编译与优化
1、前端编译与优化
1-1、概述
- 编译器是一个很含糊的表达,要放到具体的上下文环境中去讨论;
- 前端编译器:把*.java文件转变成*.class的过程;
- 即时编译器(JIT):运行期把字节码转变成本地机器码的过程;
- 提前编译器:(静态的)提前编译器(AOT)直接将程序编译成与目标机器指令相关的二进制代码的过程;
1-2、Javac编译器
- 编译过程大致分为一个准备过程和三个处理过程
- 准备过程:初始化插入式注解处理器;
- 解析与填充符号表过程,包括:
- 词法、语法分析
- 填充符号表
- 插入式注解处理器的注解处理过程:插入式注解处理器的执行阶段;
- 分析与字节码生成过程,包括:
- 标注检查;
- 数据流及控制流分析;
- 解语法糖;
- 字节码生成;
如图所示:
1、解析与填充符号表
1、词法、语法分析
- 词法分析是将源代码的字符流转变为标记(Token)集合的过程。标记是编译时的最小元素。关键字、变量名、字面量、运算符都可以作为标记。
- 词法分析过程由com.sun.tools.javac.parser.Scanner类实现;
- 语法分析是根据标记序列构造抽象语法树,抽象语法树(AST)每一个节点都代表着程序代码中的一个语法结构。
- 这个阶段产生的抽象语法树是以com.sun.tools.javac.tree.JCTree类表示的。
- 经历过着两个步骤之后,编译器就不会再对源码字符流进行操作,后续的操作都建立在抽象语法树之上。
2、填充符号表
- 符号表是由一组符号地址和符号信息构成的数据结构;
- 符号表所登记的信息在编译的不同阶段都要被用到;
- 填充符号表的过程是由com.sum.tools.javac.comp.Enter类实现;
2、注解处理器
- 我们可以把插入式注解处理器当作是一组编译器的插件,当这些插件工作时,允许读取、修改、添加抽象语法树中的任意元素。
- 如果对语法树进行修改,编译器将回到解析及填充符号表的过程重新处理,直到所有的插入式注解处理器都没有再对语法树进行修改为止;每一次循环过程被称为一个轮次对于上图中的回环过程;
- 注解的初始化过程是在initPorcessAnnotations()方法中完成的;执行过程在processAnnotations()中完成的;
3、语义分析与字节码生成
- 抽象语法树能够表示一个结构正确的源程序,但无法保证源程序的语义是符号逻辑的;
- 语义分析的主要任务:则是对结构上正确的源程序进行上下文相关性质的检查,譬如:类型检查、控制流检查、数据流检查;
1、标注检查
- 标志检查步骤要检查的内容包括注入变量使用前已被声明、变量与赋值之间的数据类型是否能够匹配, 还会顺便进行常量折叠这项优化;
- 标志检查Javac源码:com.sun.tools.javac.comp.Attr类 和com.sun.tools.javac.comp.Check类
2、数据及控制流分析
- 数据流分析和控制流分析是对程序上下文逻辑更近一步的验证,它可以检查出诸如程序局部变量在使用前是否被赋值、方法的每条路径是否都有返回值、是否所有受查异常都被正确处理了等问题。
3、解语法糖
- 最常见的语法糖是泛型、变长参数、自动装箱拆箱;
- 解语法糖的过程由desugar()方法触发,com.sun.tools.javac.comp.TransTypes类和com.sun.tools.javac.comp.Lower类完成的;
4、字节码生成
- 字节码生成是Javac编译过程中的最后一个阶段,由com.sun.tools.javac.jvm.Gen类来完成。
- 字节码生成阶段就是将前面各个步骤生成的信息转化为字节码写到磁盘中。
- 实例构造器() 和类构造器() 方法就是这个阶段被添加到语法树之中;
- 填充了所有所需信息的符号表交到com.sun.tools.javac.jvm.ClassWriter类,由writerClass方法输出字节码,生成最终的Class文件。
1-3、Java语法糖
1、泛型
- Java泛型是类型擦除式泛型,C#选择的是具现化式泛型;
2、自动装箱、拆箱及遍历循环
3、条件编译
-
Java中体哦傲剑编译 根据布尔常量值的真假,编译器将会把分支中不成立的代码消除掉。
参考资料《深入理解Java虚拟机》第三版