Java代码编译优化
»深入理解Java虚拟机读书笔记目录:
Java代码的执行大致可以分为如下过程。
- 前端编译器将.java代码编译成.class字节码
- 后端运行时编译器将.class字节码变成机器码
- 还可能是使用静态提前编译器将.java文件编译成机器码
因此,java中的编译器可以分为三类:
- 前端编译器:javac
- JIT编译器:HotSpot VM的C1、C2
- AOT编译器
这里主要讨论前两种编译器。
javac编译器
使用javac编译就是将.java文件编译成.class字节码文件的过程,javac的编译过程大致分为三步
- 解析与填充符号表过程
- 插入式注解处理器的注解处理过程
- 语义分析与字节码生成过程
解析与填充符号表过程
该过程主要有三步:
- 词法分析
- 语法分析
- 填充符号表
(1) 词法分析
该过程指的是将源代码的字符流转变为标记集合。
例如:int a = b + 2;
这段代码包含了6个标记,int,a,=,b,+,2
(2) 语法分析
语法分析指的是根据标记序列构造抽象语法树的过程
(3) 填充符号表过程
符号表是由一组符号地址和符号信息组成的表格,即可以认为是将符号信息和符号地址进行统计,便于后续的过程中获取符号
注解处理器
就是在编译期间对注解进行处理,通过注解处理器可以读取、修改、添加抽象语法树中的任意元素
语义分析与字节码生成过程
该过程主要有四步:
- 标注检查
- 数据及控制流分析
- 解语法糖
- 字节码生成
(1) 标注检查
标注检查主要是检查:
- 变量使用前是否声明
- 变量与赋值的数据类型是否匹配
- 常量折叠
其中常量折叠指的是将常量进行计算 例如:
int a = 1 + 2;
编译之后会定义a = 3;因此,与int a = 3相比,运行速度是一样的。
(2) 数据及控制流分析
该过程是对程序上下文逻辑进行进一步的检查:
- 程序局部变量在使用前是否赋值
- 方法的每条路径是否都有返回值
- 是否所有的受查异常都能被正确处理
(3) 解语法糖
语法糖可以看作是编译器的小把戏,为了方便程序员编写代码,增加程序的可读性
例如:泛型机制
泛型的本质是参数化类型,就是将操作的数据类型指定为一个参数
在早前没有泛型的之后,程序员需要通过指定Object类型,然后强转来实现特定类型的数据的读取,这样只有运行的时候才能发现某些类型的错误。
而加入泛型之后可以让错误在编译时就被发现。但是在java中泛型只会存在与源码中,编译之后的字节码是不存在泛型的,也就是说字节码里面仍然是通过强转Object类型来实现。即运行时ArrayList
此外还有自动装箱、拆箱、foreach循环等都是语法糖
(4) 字节码生成
该过程将前面生成的信息转换成字节码写到磁盘,同时还会进行一些代码的替换优化,例如将字符串的相加替换为StringBuilder的append操作
即时编译器
早先,java程序是通过解释器进行解释执行。即直接解释字节码,从而运行程序。而后来,为了提高热点代码(运行特别频繁的代码)的执行效率,在运行时,虚拟机会将这部分热点代码编译成与本地平台相关的机器码,并进行各种优化,完成这个任务的编译器叫即使编译器(JIT)
即时编译器并不是虚拟机必备的,但是是衡量虚拟机性能的一个很重要的指标。
(1) 解释器与编译器
主流的虚拟机都采用解释器与编译器共存的架构。
当程序需要迅速启动和执行的时候,使用解释器更有优势,省去编译时间。在程序后台运行的时候,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码后,可以获得更高的执行效率。解释器与编译器通常都是配合使用。
HotSpot内置两个即时编译器,Client Compiler、Server Compiler简称C1、C2。通常使用解释器与其中一个编译器直接配合的方式工作。
为了在程序启动响应速度和运行效率之间达到最佳平衡,HotSopt采用分层策略。
- 第0层:程序解释执行
- 第1层:也称C1编译,将字节码编译为本地代码,进行简单,可靠的优化
- 第2层:也称C2编译,将字节码编译为本地代码,启动一些耗时较长的优化。
实施分层编译后,C1、C2将会同时工作,许多代码会被多次编译,用C1获取更高的编译速度,C2获取更好的编译质量。
(2) 编译触发的条件
在运行时会被即时编译器编译的热点代码有两种:
- 多次调用的方法体
- 多次执行的循环体
对于后一种情况,尽管编译是由循环体触发,但编译器依然会以整个方法作为编译对象,这种编译方式发生在方法执行过程中,称之为“栈上替换”,即方法栈帧还在栈上,方法就被替换了。
判断一段代码是不是热点代码:
- 基于采样的热点探测:虚拟机周期性的检查各个线程的栈顶,如果某个方法经常出现在栈顶,那么就是热点方法。容易受到线程阻塞的干扰。
- 基于计数器的热点探测:虚拟机为每个方法建立计数器,统计方法的执行次数,如果方法超过一定的值就是热点方法。
通常采用基于计数器的热点探测。
计数器分为:方法调用计数器、回边计数器