早期(编译期)优化——Javac编译器

本文深入探讨了Java编译过程,从源代码解析到符号表填充,再到注解处理、语义分析及字节码生成,全面解析javac的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


“编译期”是一个不确定的过程:

  • 前端编译器:把.java转换为.class的过程。如Sun的javac、Eclispe JDT的ECJ
  • 后端运行期间编译器:将.class转换为机器码的过程。Hotspot VM的C1、C2编译器。
  • 静态提前编译器:直接把.java转换为本地机器码的过程。CNU Compiler for the Java(GCJ)、Excelsior JET。

一、Javac的源码与调试

Javac的源码存放在

JDK_SRC_HOME/langtools/src/share/classes/com/sun/tools/j>avac中。还有javac不像hotspot那样是用C++实现的,javac是纯>java代码实现的。

编译过程:

java代码
解析与填充符号表
注解处理
分析与字节码生成
机器码

JDK7中Javac编译过程的主体代码(编译动作的入口是som.sun.tools.javac…main.JavaCompiler):
compile

try {
            //准备过程:初始化插入式注解处理器
            initProcessAnnotations(processors);

            // These method calls must be chained to avoid memory leaks
            delegateCompiler =
                processAnnotations( //2:执行注解过程
                    enterTrees(stopIfError(CompileState.PARSE,//过程1.2:输入到符号表
                            parseFiles(sourceFileObjects))),//过程1.1:词法分析、语法分析
                    classnames);

            delegateCompiler.compile2();//过程3:分析及字节码生成
            delegateCompiler.close();
            elapsed_msec = delegateCompiler.elapsed_msec;
        }

compile2():

case BY_TODO:
                while (!todo.isEmpty())
                    generate(desugar(flow(attribute(todo.remove()))));//过程3.1-attribute:标注;过程3.2-flow:数据流分析;过程3.3-desugar:解语法糖;过程3.4-generate:生成字节码
                break;

二、解析与填充符号表

2.1 词法分析、语法分析

词法、语法分析对应parseFiles()
词法分析:

词法分析就是将源码转为(Token)标记,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,关键词、变量名、字面量、运算符都可以成为标记。

语法分析:

就是根据Token构造抽象语法树的过程,语法树的每一个节点都代表着程序中的一个语法结构,例如包、类型、修饰符、运算符、接口、返回值甚至代码注释。

2.2 填充符号表

填充符号表对应enterTrees()
符号表是由一组符号地址符号信息构成的表格。符号表在编译的不同阶段都要用到。


三、注解处理器

注解与代码一样都是在运行期间发挥作用的。插入式注解处理器可以在编译期间对注解进行处理。其工作原理如下:

如果在注解处理期间对语法书进行了修改,那么编译器将进行一次循环Round,回到解析及填充符号表阶段。直到注解处理期间不在修改语法树。

插入式注解处理器的初始化是在==initProcessAnnotations()中完成的,而注解处理过程是在processAnnotations()==方法中完成的。


四、语义分析与字节码生成

语法树表示一个结构正确的程序的抽象,但无法保证源程序是符合逻辑的。所以就有了语义分析。语义分析包括标注检查数据及控制流分析

例子:

int a=1;
boolean b=false;
char c=2;

int d=a+c; //1
int d=b+c;//2
char d=a+c;//3

从语法、词法分析中,这是没毛病的,但是不和逻辑啊!!!1,2,3中只有1在语义上没毛病。

4.1 标注检查

对应的是attribute()
标注检查:

标注检查的内容有检查变量在使用前是否已被声明、变量与赋值类型是否匹配等。

标注检查中,还有一个重要的动作——常量折叠。例子:

int a=1+2;

这个在词法解析、语法解析之后的语法树中,'1’、‘+’、‘2’是存在的。然而在标注检查时,经过常量折叠后,会被折叠为字面量‘3’。

4.2 数据及控制流分析

对应的是flow()
数据及控制流解析:

这是对程序上下文逻辑更近一步的验证。它可以检查出程序局部变量在使用前是否被赋值、方法的每条路径是否有返回值、是否所有的可检查异常都被正确处理等问题。

前端编译器和后端运行期间编译器在数据及控制流分析的目的上是一致的,但校验范围有所区别。有一些校验项只有在编译期或运行期才能进行。例子:

//带有final
public void foo(final int arg){
	final int var=0;
}

//没有final修饰
public void foo(int arg){
	int var=0;
}

这两段代码编译出来的class文件是没有区别的。局部变量声明为final,对运行是没有影响的,变量的不变性由编译器在编译期保障

4.3 解语法糖

对应的是desugar()
语法糖只是让程序员更好的编写代码。Java中最常见的语法糖就是泛型、变长参数、自动装箱/拆箱。虚拟机运行时并不支持这些语法。所以在编译阶段还原会简单的基础语法结构这一过程就是解语法糖。

4.4 字节码生成

对应generate()
字节码生成:

除了将前面各个步奏生成的信息(语法树、符号表)转换为字节码写到磁盘中,编译器还进行了少量的代码添加(如实例构造器<init>()、类构造器<clinit>)和转化工作(如把字符串的加操作转化为StringBuffer或StringBuilder的append()操作)。

### 编译器与解释器的基本工作流程 #### 编译器的工作流程 编译器是一种计算机程序,它能够将高级编程语言书写的源代码转换为目标代码或机器码。这一过程涉及多个阶段: - **预处理**:此阶段会处理宏定义、文件包含等操作,准备后续的编译步骤[^2]。 - **词法分析**:这是编译的第一步,在这里输入的字符流被解析成有意义的标记序列,即把字符串形式的语言元素转化为内部表示的形式[^3]。 - **语法分析**:紧接着词法分析之后,该环节负责验证这些标记是否遵循语法规则,并构建抽象语法树来描述源代码结构[^1]。 - **中间代码生成**:有时为了简化优化过程或者支持跨平台特性,会在语法分析后创建一种介于高层语言和低层汇编之间的中间表示(IR)。 - **代码优化**:在此期间会对IR进行各种变换以提高最终输出的质量,比如减少冗余计算、调整循环展开程度等。 - **目标代码生成**:最后一步是从优化后的IR生成特定硬件架构上的二进制指令集——也就是常说的目标代码;随后还需经过链接阶段才能形成完整的可执行文件[^4]。 ```cpp // 示例C++代码片段展示简单的编译过程 #include <iostream> using namespace std; int main() { cout << "Hello, world!" << endl; return 0; // 经过上述各阶段变为机器码并打包为exe或其他格式 } ``` #### 解释器的工作流程 相比之下,解释器并不事先将整个程序翻译成另一种形式存储起来再运行,而是逐条读取源代码中的每一条命令并立即执行之。这意味着对于某些脚本语言来说,每次启动应用程序都需要重新解析一遍原始文本直到遇到错误为止。不过现代很多所谓的“解释型”语言实际上也包含了某种形式的部分提前编译机制(如Python字节码),只是不像传统意义上的全量编译那样彻底而已。 具体到像Java这样的混合模式,则先由javac工具完成一次初步编译得到.class字节码文件,当JVM加载此类时才真正激活其内置的即时(JIT)编译功能进一步转化成本地机器指令供CPU直接调用,从而实现了既保留灵活性又兼顾性能的优势[^5]。 ### 编译器与解释器的区别总结 | 特征 | 编译器 | 解释器 | | --- | --- | --- | | 执行方式 | 将全部源代码一次性转为机器码后再整体运行 | 边读边解密单行或多行原生表述直至结束 | | 运行速度 | 更快,因为已经完成了所有必要的准备工作 | 较慢,由于存在持续性的动态评估开销 | | 错误检测时机 | 编译期就能发现大部分逻辑缺陷 | 只能在实际尝试对应部分的时候才会暴露问题 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值