11.5 实战:深入理解Graal编译器 11.5.1 历史背景

11.5 实战:深入理解Graal编译器

在本书刚开始介绍HotSpot即时编译器的时候曾经说过,从JDK 10起,HotSpot就同时拥有三款不同的即时编译器。此前我们已经介绍了经典的客户端编译器和服务端编译器,在本节,我们将把目光聚焦到HotSpot即时编译器以及提前编译器共同的最新成果——Graal编译器身上。

11.5.1 历史背景

在第1章展望Java技术的未来时,我们就听说过Graal虚拟机以及Graal编译器仍在实验室中尚未商用,但未来其有望代替或成为HotSpot下一代技术基础。Graal编译器最初是在Maxine虚拟机^1中作为C1X编译器[^2]的下一代编译器而设计的,所以它理所当然地使用于Java语言来编写。2012年,Graal编译器从Maxine虚拟机项目中分离,成为一个独立发展的Java编译器项目[^3],Oracle Labs希望它最终能够成为一款高编译效率、高输出质量、支持提前编译和即时编译,同时支持应用于包括HotSpot在内的不同虚拟机的编译器。由于这个编译器使用Java编写,代码清晰,又继承了许多来自HotSpot的服务端编译器的高质量优化技术,所以无论是科技企业还是高校研究院,都愿意在它上面研究和开发新编译技术。HotSpot服务端编译器的创造者Cliff Click自己就对Graal编译器十分推崇,并且公开表示再也不会用C、C++去编写虚拟机和编译器了。Twitter的Java虚拟机团队也曾公开说过C2目前犹如一潭死水, 亟待一个替代品,因为在它上面开发、改进实在太困难了。

Graal编译器在JDK 9时以Jaotc提前编译工具的形式首次加入到官方的JDK中,从JDK 10起,Graal 编译器可以替换服务端编译器,成为HotSpot分层编译中最顶层的即时编译器。这种可替换的即时编译器架构的实现,得益于HotSpot编译器接口的出现。

早期的Graal曾经同C1及C2一样,与HotSpot的协作是紧耦合的,这意味着每次编译Graal均需重新编译整个HotSpot。JDK 9时发布的JEP 243:Java虚拟机编译器接口(Java-Level JVM Compiler Interface,JVMCI)使得Graal可以从HotSpot的代码中分离出来。JVMCI主要提供如下三种功能:

  • 响应HotSpot的编译请求,并将该请求分发给Java实现的即时编译器。
  • 允许编译器访问HotSpot中与即时编译相关的数据结构,包括类、字段、方法及其性能监控数据 等,并提供了一组这些数据结构在Java语言层面的抽象表示。
  • 提供HotSpot代码缓存(Code Cache)的Java端抽象表示,允许编译器部署编译完成的二进制机器 码。

综合利用上述三项功能,我们就可以把一个在HotSpot虚拟机外部的、用Java语言实现的即时编译器(不局限于Graal)集成到HotSpot中,响应HotSpot发出的最顶层的编译请求,并将编译后的二进制代码部署到HotSpot的代码缓存中。此外,单独使用上述第三项功能,又可以绕开HotSpot的即时编译系统,让该编译器直接为应用的类库编译出二进制机器码,将该编译器当作一个提前编译器去使用 (如Jaotc)。

Graal和JVMCI的出现,为不直接从事Java虚拟机和编译器开发,但对Java虚拟机技术充满好奇心的读者们提供一条窥探和尝试编译器技术的良好途径,现在我们就将开始基于Graal来实战HotSpot虚拟机的即时编译与代码优化过程。

[^2]: C1X是Maxine虚拟机照着HotSpot C1编译器实现的编译器。
[^3]: 相关资料:https://jaxenter.com/oracle-championing-cause-for-graal-to-be-part-of-openjdk-104172.html。