32、JVM编译器优化
32.1、JVM编译的过程
1、解析与填充符号表过程
1)、词法、语法分析
词法分析将源代码的字符流转变为标记集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,javac中由com.sun.tools.javac.parser.Scanner类实现。
语法分析是根据token序列构造抽象语法树的过程。抽象语法树(AST)是一种用来描述程序代码语法结构的树形表示方式,语法树中的每一个节点都代表着程序代码中的语法结构,javac中,语法分析过程由com.sun.tools.javac.tree.parser.Parser类实现,这个阶段产生出的抽象语法树由com.sun.tools.javac.tree.JCTree类表示。
2)、填充符号表
enterTree()方法,符号表是由一组符号地址和符号信息构成的表格,符号表中登记的信息在编译的不同阶段都要用到。在语义分析中,符号表所登记的内容将用于语义检查和产生中间代码,在目标代码生成阶段,当对符号进行地址分配时,符号表是地址分配的依据。javac源码中由com.sun.tools.javac.comp.Enter类实现。
2、插入式注解处理器的注解处理过程
注解在运行期间发挥作用,通过插入式注解处理器标准API可以读取、修改、添加抽象语法树种的任意元素,若在处理注解期间对语法树进行修改,编译器将回到解析即填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个round。javac源码中插入式注解处理器的初始化过程是在initProrcessAnnotation()方法中完成的,而它的执行过程则是在processAnnotation()方法中完成。
3、分析与字节码生成过程
1)、标注检查
attribute()方法,标注检查步骤检查的内容包括诸如变量使用前是否已经被声明、变量与赋值之间的数据类型是否够匹配以及常量折叠。javac中实现类是com.sun.tools.javac.comp.Attr类和com.sun.tools.javac.comp.Check类。
2)、数据及控制流分析
flow()方法,对程序上下文逻辑更进一步的验证,他可以检查出诸如程序局部变量在使用前是否赋值、方法的每条路径是否都有返回值、是否所有的受检查异常都被正确处理了问题。
局部变量在常量池中没有CONSTANT_Fieldref_info的符号引用,自然没有访问标志的信息,甚至可能连名称都不会保存下来。
将局部变量声明为final,对运行期是没有影响的,变量的不变性仅仅由编译器在编译期间保障。
3)、解语法糖
也称糖衣语法,指在计算机中添加某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用,通常来说,使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。java中最常用的是泛型、变长参数、自动装箱/拆箱等。
4)、字节码生成
javac编译的最后一个阶段,javac源码里面由com.sun.tools.javac.jvm.Gen类来完成,这个阶段不仅仅把前面各个步骤所生成的信息转化成字节码写到磁盘中,编译器还进行少量的代码添加转换工作。
保证一定是按先执行父类的实例构造器,然后初始化变量,最后执行语句块的顺序进行。
32.2、javac语法糖
1、泛型与类擦除
java中的泛型它只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型,并在相应的地方插入了强制转换代码,对于运行期的java来说ArrayList与ArrayList就是同一个类,java语言中的泛型实现方法称为类型擦除,基于这种方法的叫伪泛型。
在Class文件格式中,只要描述符不是完全一致的两个方法就可以共存。
Signature是解决伴随泛型而来的参数类型的识别问题中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息、擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能够通过反射手段取得参数化类型的根本依据。
2、自动装箱、拆箱与循环遍历
3、条件编译
java编译器并非一个个地编译Java文件,而是将所有编译单元的语法树顶级节点输入到待处理列表后再进行编译,因此各个文之间能够互相提供符号信息。
java中根据布尔常量值的真假,编译器会把分支中不成立的代码块擦除掉。这一工作将在编译器解除语法糖阶段完成。
4、常用语法糖
泛型、自动装箱、自动拆箱、遍历循环、变长参数、条件编译、内部类、枚举类、断言语句、对枚举、字符串的switch,try与catch定义和关闭资源。
32.3、运行期JIT编译器
java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或者代码块的运行特别频繁时,就好把这些代码认定为“热点代码”,为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(JIT编译器),他是虚拟机中最核心且最能体现虚拟机水平的部分。
JIT 是 just in time 的缩写,也就是即时编译器。使用即时编译器技术,能够加速Java 程序的执行速度。
首先,我们大家都知道,通常通过 javac 将程序源代码编译,转换成 java 字节码,JVM 通过解释字节码将其翻译成对应的机器指令,逐条读入,逐条解释翻译。很显然,经过解释执行,其执行速度必然会比可执行的二进制字节码程序慢很多。为了提高执行速度,引入了 JIT 技术。
在运行时 JIT 会把翻译过的机器码保