32
32-Java编译器之GraalGraal 编译器GraalVM 是⼀个⾼性能的、⽀持多种编程语⾔的执⾏环境。它既可以在传统的 OpenJDK 上运⾏,也可以通过 AOT(Ahead-Of-Time)编译成可执⾏⽂件单独运⾏,甚⾄可以集成⾄数据库中运⾏Graal 编译器:GraalVM 的基⽯⽤ Java 写就的即时编译器,它从 Java 9u 开始便被集成⾃ JDK 中,作为实验性质的即时编译器可以通过 Java 虚拟机参数-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler启⽤当启⽤时,它将替换掉 HotSpot 中的 C2 编译器,并响应原本由 C2 负责的编译请求Graal 和 Java 虚拟机的交互即时编译器是 Java 虚拟机中相对独⽴的模块负责接收 Java 字节码⽣成可以直接运⾏的⼆进制码即时编译器与 Java 虚拟机的交互可以分为:1. 响应编译请求;2. 获取编译所需的元数据(如类、⽅法、字段)和反映程序执⾏状态的 profile3. 将⽣成的⼆进制码部署⾄代码缓存(code cache)⾥即时编译器通过这三个功能组成了⼀个响应编译请求、获取编译所需的数据,完成编译并部署的完整编译周期传统情况下,即时编译器是与 Java 虚拟机紧耦合的为了让 Java 虚拟机与 Graal 解耦合,引⼊了Java 虚拟机编译器接⼝(JVM Compiler Interface,JVMCI),上述三个功能抽象成⼀个Java 层⾯的接⼝在 Graal 所依赖的 JVMCI 版本不变的情况下,我们仅需要替换 Graal 编译器相关的 jar 包(Java 9 以后的 jmod ⽂件),便可完成对Graal 的升级JVMCI 的作⽤并不局限于完成由 Java 虚拟机发出的编译请求。Java 程序可以直接调⽤ Graal,编译并部署指定⽅法Graal 的单元测试便是基于这项技术Graal 和 C2 的区别Graal 是⽤ Java 写的,⽽ C2 是⽤ C++ 写的Graal 更加模块化,也更容易开发与维护在充分预热的情况下,Java 程序中的热点代码早已经通过即时编译转换为⼆进制码,在执⾏速度上并不亚于静态编译的 C++ 程序即便是解释执⾏ Graal,也仅是会减慢编译效率,⽽并不影响编译结果的性能如果 C2 和 Graal 采⽤相同的优化⼿段,那么它们的编译结果是⼀样的Graal 和 C2 另⼀个优化上的分歧则是⽅法内联算法。相对来说,Graal 的内联算法对新语法、新语⾔更加友好,例如 Java 8 的 lambda 表达式以及 Scala 语⾔总体⽽⾔,Graal 编译结果的性能要优于 C2。对于 Java 程序来说,Graal 的优势并不明显;对于 Scala 程序来说,Graal 的性能优势达到了 10%Graal 的实现Graal 编译器将编译过程分为前端和后端两⼤部分前端⽤于实现平台⽆关的优化(如⽅法内联),以及⼩部分平台相关的优化后端则负责⼤部分的平台相关优化(如寄存器分配),以及机器码的⽣成Graal 和 C2 都采⽤了 Sea-of-Nodes IRGraal 的前端是由⼀个个单独的优化阶段(optimization phase)构成的我们可以将每个优化阶段想象成⼀个图算法:它会接收⼀个规则的图,遍历图上的节点并做出优化,并且返回另⼀个规则的图前端中的编译阶段除了少数⼏个关键的之外,其余均可以通过配置选项来开启或关闭Graal 和 C2 都采⽤了激进的投机性优化⼿段(speculative optimization)Graal 与 C2 相⽐会更加激进。它从设计上便⼗分青睐这种基于假设的优化⼿段。在编译过程中,Graal ⽀持⾃定义假设,并且直接与去优化节点相关联编译,优化,编译器,请求,平台,情况,相关,性能