Java编译器的内部架构

Jag*_*esh 8 java javacompiler

我已经工作Java了8年多.

上周,在我公司的一次小型会议上,我的一位同事问我究竟是如何Java Compiler运作的?我没有回答.

我试图解释,如Java编译器会通过一个语句之一,并将其转换为未针对任何字节码OS,但到JVM.

即使是我也没有人满意这个答案.

现在主要问题是java编译器的工作原理.即,在编译Java文件的情况下,编译器将执行多少步骤或阶段或阶段.

究竟什么Java's compiler建筑?

如果Java classes同一个.java文件中有多个怎么办?然后将编译多少个类.

如果有导入指向未编译的Java类怎么办?然后编译或忽略未编译的类?

我用Google搜索了半天以上,所有人都提供了与我给同事相同的答案.

但最后我在这里找到了一些有用的教程.

但是教程也没有太深入,我无法想象该教程.

我仍然不满意,并渴望从你身上学到更多关于此事的信息.

因此,如果任何人比我和上述博客更了解某些东西,那么通过使用我可以直观地了解内部架构的内容,Java Compiler请解释一下.

ACV*_*ACV 8

一些基本步骤:

  1. parse:读取一组*.java源文件,并将生成的标记序列映射到AST(抽象语法树)-Nodes.
  2. enter:将定义的符号输入符号表.
  3. process annotations:如果Requested,则处理在指定的编译单元中找到的注释.
  4. attribute:属性语法树.此步骤包括名称解析,类型检查和常量折叠.
  5. flow:对上一步中的树执行数据流分析.这包括检查分配和可达性.
  6. desugar:重写AST并翻译掉一些语法糖.
  7. generate:生成源文件或类文件.

更多细节:

  1. Lex - 将源文件分成单个单词或标记.
  2. 解析 - 分析程序的短语结构.
  3. 语义动作 - 构建一个对应于每个短语的抽象语法树.
  4. 语义分析 - 确定每个短语的含义,将变量的使用与其定义联系起来,检查表达式的类型,请求每个短语的翻译.
  5. 帧布局 - 以与机器相关的方式将变量,函数参数等放入激活记录(堆栈帧)中.
  6. 翻译 - 生成中间表示树(IR树),这是一种与任何特定源语言或目标机器结构无关的符号.
  7. Canonicalize - 提升表达式的副作用,并清理条件分支,以方便下一阶段.
  8. 指令选择 - 将IR树节点分组为与目标机器指令的动作相对应的块.
  9. 控制流分析 - 将指令序列分析到控制流程图中,该控制流程图显示程序执行时可能遵循的所有可能的控制流程.

  10. 数据流分析 - 通过程序变量收集有关信息流的信息; 例如,活跃度分析计算每个程序变量保持仍然需要的值(是活动的)的位置.

  11. 寄存器分配 - 选择一个寄存器来保存程序使用的每个变量和临时值; 不同时存在的变量可以共享同一个寄存器.

  12. 代码发射 - 用机器寄存器替换每个机器指令中的临时名称.

有一本好书:

Java中的现代编译器实现

你可能想看看里面的javac代码:

Javac文档

OpenJDK源代码

黑客的javac指南

不要惊慌!为了帮助新手javac在代码库中导航

JVM JLS


Dav*_*INO 6

编译器有不同的步骤,但这里最重要的是:

词法分析 第一步是词法分析.基本上这个步骤从java代码中提取标记(关键字,运算符,分隔符,注释,变量名......)

语法分析(parser) 第二步是语法分析.标记被作为词法分析的输入,并被组合以形成表达和指令.

优化和转换为字节代码 最后一个宏步骤是将上一步转换为字节代码.这里的代码可以修改为与原始代码等效,但效率更高.


注意:此过程与java无关,但对所有编译器都是通用的.也是不生成中间字节代码但是生成机器代码的编译器(比如C或C++的编译器).

通常有工具来创建词法分析器和语法分析器,因为这些步骤在不同语言之间有许多公共部分.

一个开源词汇分析器是flex 一个有用的句法分析器是yacc

两者都使用C和C++作为最常用的语言来创建编译器(java和其他语言),但是对于其他编程语言也有类似的替代方法(另一种语言创建编译器,而不是用于另一种语言).基本上编写编译器的语言与编译器编译的语言无关.