Javac编译器
Javac 编译过程⼤致可以分为1个准备过程和3个处理过程:
1. 准备过程:初始化插⼊式注解处理器。2. 解析与填充符号表过程,包括:
1. 词法、语法分析,将源代码的字符流转变为标记集合,构造出抽象语法树。2. 填充符号表,产⽣符号地址和符号信息。
3. 插⼊式注解处理器的注解处理过程。可以把插⼊式注解处理器看作是⼀组编译器的插件,当这些插件⼯作时,允许读取、修改、添加抽象语法树中的任意元素,如Lombok注解。4. 分析与字节码⽣成过程,包括:
1. 标注检查。对语法的静态信息进⾏检查。
2. 数据流及控制流分析。对程序动态运⾏过程进⾏检查。3. 解语法糖。将简化代码编写的语法糖还原为原有的形式。4. 字节码⽣成。将前⾯各个步骤所⽣成的信息转化成字节码。上述3个处理过程⾥,执⾏插⼊式注解时⼜可能会产⽣新的符号,如果有新的符号产⽣,就必须转回到之前的解析、填充符号表的过程中重新处理这些新符号。javac编译过程如下:图1-1 Javac 编译过程注解处理器
在JDK 6中提出并通过了JSR-269提案,该提案设计了⼀组被称为“插⼊式注解处理器”的标准API,可以提前⾄编译期对代码中的特定注解进⾏处理,从⽽影响到前端编译器的⼯作过程。可以把插⼊式注解处理器看作是⼀组编译器的插件,当这些插件⼯作时,允许读取、修改、添加抽象语法树中的任意元素。
如果这些插件在处理注解期间对语法树进⾏过修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插⼊式注解处理器都没有再对语法树进⾏修改为⽌。典型⽰例为Lombok,它可以通过注解来实现⾃动产⽣getter/setter⽅法、进⾏空置检查、⽣成受查异常表、产⽣equals()和hashCode()⽅法等等。
要通过注解处理器API实现⼀个编译器插件,实现注解处理器的代码需要继承抽象类javax.annotation.processing.AbstractProcessor并重写其抽象⽅法。
即时编译器
⽬前主流的两款商⽤Java虚拟机(HotSpot、OpenJ9)⾥,Java程序最初都是通过解释器(Interpreter)进⾏解释执⾏的,当虚拟机发现某个⽅法或代码块的运⾏特别频繁,就会把这些代码认定为热点代码(Hot Spot Code),为了提⾼热点代码的执⾏效率,在运⾏时,虚拟机将会把这些代码编译成本地机器码,并以各种⼿段尽可能地进⾏代码优化,运⾏时完成这个任务的**后端编译器被称为即时编译器。
解释器与编译器
HotSpot 虚拟机内部同时包含解释器与编译器,两者相辅相成地配合完成⼯作。解释器与编译器两者各有优势:当程序需要迅速启动和执⾏的时候,解释器可以⾸先发挥作⽤,省去编译的时间,⽴即运⾏。当程序启动后,随着时间的推移,编译器逐渐发挥作⽤,把越来越多的代码编译成本地代码,这样可以减少解释器的中间损耗,获得更⾼的执⾏效率。当程序运⾏环境中内存资源限制较⼤,可以使⽤解释执⾏节约内存(如部分嵌⼊式系统中和⼤部分的JavaCard应⽤中就只有解释器的存在),反之可以使⽤编译执⾏来提升效率。图1-2 解释器与编译器的交互
HotSpot虚拟机中内置了两个即时编译器,分别被称为“客户端编译器”(Client Compiler)和“服务端编译器”(Server Compiler),或者简称为C1编译器和C2编译器。C1拥有更快地编译速度,⽽C2拥有更好地编译质量。⽤户也可以使⽤“-client”或“-server”参数去强制指定虚拟机运⾏在客户端模式还是服务端模式。
⽆论采⽤的编译器是客户端编译器还是服务端编译器,解释器与编译器搭配使⽤的⽅式在虚拟机中被称为“混合模式”(Mixed Mode),也可以使⽤参数“-Xint”强制虚拟机运⾏于“解释模式”(Interpreted Mode),这时候编译器完全不介⼊⼯作,全部代码都使⽤解释⽅式执⾏。另外,也可以使⽤参数“-Xcomp”强制虚拟机运⾏于“编译模式”(Compiled Mode),这时候将优先采⽤编译⽅式执⾏程序,但解释器仍要在编译⽆法进⾏的情况下介⼊执⾏过程。
因篇幅问题不能全部显示,请点此查看更多更全内容