JVM读书笔记(六)——早期(编译期)优化

6.1 概述

Java语言的编译期实际上是一段不确定的操作过程,因为它可能是指一个编译器的前端把*.java转变成*.class文件的过程,也可能是虚拟机的后端运行期的编译器(JIT编译器)把字节码转变成机器码的过程,还可能是指使用静态提前编译器直接把*.java文件编译成本地机器代码的过程。

其中最符合大家对Java程序编译认知的应是第一种,本文所将的编译期和编译器都仅限第一种。

6.2 Javac编译器

6.2.1 Javac的源码与调试

Javac的源码存放在JDK_SRC_HOME/langtools/src/share/classes/lang/tools/javac中,除了jdk自身的api之外,就只引用JDK_SRC_HOME/langtools/src/share/classes/com/sun/*里面的代码,调试环境建立起来简单方便,因为基本上不需要处理依赖关系。

6.2.2 解析和填充符号表

1.词法、语法分析

词法分析是将源代码的字符流转化为标记集合,单个字符时程序编写过程的最小元素,而标记则是编译过程的最小元素。

语法分析是根据Token序列构造抽象语法树的过程,抽象语法树是一种用来描述程序代码语法结构的树形表示方法,语法树的每一个节点都代表着程序代码中的一个语法结构。

2.填充符号表

符号表是由一组符号地址和符号信息构成的表格,可以想象成K-V形式。符号表中所登记的信息在编译的不同阶段都要用到。在语义分析中,符号表所登记的内容将用于语义检查和产生中间代码。在目标代码生成阶段,当对符号名进行地址分配时,符号表示地址分配的依据。

6.2.3 注解处理器

JDK提供了一种插入式注解处理器的标准API在编译期间对注解进行处理,可以把它看做是一组编译器的插件,在这些插件里,可以读取、修改、添加抽象语法树的任意元素。如果这些插件在处理注解期间对语法树进行了修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有对语法树进行修改为止,每一次循环称为一个round。

6.2.4 语义分析与字节码生成

1.标注检查

Javac的编译过程中,语义分析过程分为标注检查以及数据及控制流分析两个步骤。标注检查的内容包括诸如变量使用前是否已被声明、变量与数据之间的赋值类型是否能够匹配等。

2.数据及控制流分析

数据及控制流分析是对程序上下文逻辑更进一步的验证,它可以检查诸如局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。编译及控制流分析与类加载时的数据及控制流分析的目的基本上是一致的,但校验范围有所区别,有一些校验项只有在编译期或运行期才能进行。

3.解语法糖

在javac的源码中,解语法糖的过程由desugar()触发,在com.sun.tools.javac.comp.TransTypes类和com.sun.tools.javac.comp.Lower类中完成。

4.字节码生成

字节码生成是Javac编译过程的最后一个阶段,在Javac源码里面由com.sun.tools.javac.jvm.Gen类来完成。字节码生成阶段不仅仅把前面各个步骤所生成的信息转化成字节码写的磁盘中,编译器还进行了少量的代码添加和转换工作。

6.3 Java语法糖

6.3.1 泛型与类型擦除

Java语言中的泛型只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型,并且在相应的地方插入了强制转型代码,因此,对于运行期的Java语言来说,ArrayList<List>与ArrayList<String>就是同一个类,所以泛型技术实际上是Java语言的一颗语法糖,Java语言中泛型实现的方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。

6.3.2 自动装箱、拆箱和遍历循环
6.3.3 条件编译

Java语言找那个条件编译的实现,也是Java语言的一块语法糖,根据布尔常量值的真假,编译器将会把分支中不成立的代码块消除掉,这一工作将在编译器解除语法糖阶段完成。由于这种条件编译的实现方式使用了if语句,所以它必须遵循最基本的Java语法,只能写在方法内部,因此它只能实现语句基本块级别的条件编译,而没有办法实现根据条件调整整个Java类的结构。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wuqingdeqing/article/details/79962565
个人分类: JVM读书笔记
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭