GraalVM与Java静态编译:原理与应用
上QQ阅读APP看书,第一时间看更新

3.1 子项目与组件

打开GraalVM在GitHub上的主页(https://github.com/oracle/graal)可以看到如图3-1所示的目录结构,其中的compiler、espresso、substratevm、sulong、truffle和wasm等目录对应各个子项目,其他目录则保存了辅助性的内容。这些子项目相互配合,构建出了GraalVM的各项功能。本节会简要介绍各个子项目。

038-01

图3-1 GraalVM目录结构

(1)Compiler

Compiler子项目全称GraalVM编译器,是用Java语言编写的Java编译器。它可以运用于多个场合,具体如下。

  • 集成到OpenJDK的HotSpot JVM中取代现有的C2编译器,既可以在运行时进行JIT编译,也可以在运行前提供AOT编译。这是GraalVM项目诞生的最初目的之一。因为C2编译器具有结构复杂、不易理解、难以调试等缺点,Oracle实验室尝试开发一款结构更加清晰,代码更易阅读理解的新编译器以取代C2,所以有了现在的GraalVM编译器。目前Twitter和Facebook等企业已经在部分服务器上使用GraalVM编译器作为它们的JVM JIT编译器,并正在致力于进一步大规模推广,最终在全部机器上使用GraalVM编译器。
  • 作为由Truffle框架实现的解释器的编译器。Truffle框架在解释执行之外,依然依赖于GraalVM编译器为其提供底层的JIT编译。
  • 作为Substrate VM静态编译框架的编译器。Substrate VM使用GraalVM编译器作为其静态编译器,在离线状态下将Java程序的字节码编译为本地代码。

人们会对用Java语言编写的编译器性能产生怀疑,但是GraalVM编译器与C2相比并没有在JIT编译时使用更多的系统资源,产生的代码质量总体上也与C2相当,甚至更优(根据Facebook的报告,GraalVM编译器在Spark的场景中相比C2有5%的性能提升[1])。因为GraalVM编译器本身也是一个Java程序,所以它可以被Substrate VM静态编译为本地库文件(称为libgraal),从而进一步提升运行时性能,降低运行时内存使用,减小对主体程序的影响。

(2)Truffle

Truffle是一个解释器实现框架。它提供了解释器的开发框架接口,可以帮助开发人员用Java为自己感兴趣的语言快速开发出语言解释器,进而可以使用GraalVM编译器进行JIT编译优化,从而得到高效的运行时性能。在图2-2右侧,Truffle Framework对其上的那些多语言的支持都是由Truffle实现的。

(3)Espresso

Espresso是从GraalVM 21.0开始引入的子项目,对应于图2-2中用圈标出的Java on Truffle部分。Espresso子项目是一个基于Truffle框架开发的,符合Java 8和Java 11规范的Java字节码解释器,可以对热点函数开启GraalVM编译器的JIT编译。目前Espresso已经具备运行Java应用程序的能力,而且通过了Java 8和Java 11的运行时兼容性测试。我们将Espresso本身运行所需的JVM称为基础JVM,将通过Espresso执行的Java程序称为客体程序,那么基础JVM与客体程序的Java版本不必相同。比如在JDK8的基础JVM上运行Java 11客体程序,或者在JDK11的基础JVM上运行Java 8的客体程序都是可行的。由此可以实现无须升级JDK而可以在旧版本JDK上执行高版本程序的能力,这对很多出于稳定性考虑而不敢轻易升级JDK的应用场景是一个福音。更进一步来说,用基础JVM的Espresso解释一个客体的Espresso也是可行的,因为Espresso自己也是Java程序。理论上这样就可以实现无限层的Java程序嵌套,只是每套一层性能会大幅降低,在嵌套了三层的Espresso上执行HelloWorld程序需要花15分钟左右,可见其运行时的性能还需要进一步提升。

(4)Substrate VM

静态编译框架Substrate VM子项目的主目录是substratevm,是本书的主角。Substrate VM提供了将Java程序静态编译为本地代码的编译工具链,包括了编译框架、静态分析工具、C++支持框架及运行时支持等。但是Substrate VM中并没有编译器和链接器,因为其编译器是GraalVM编译器,而链接器则使用了GCC(在Linux系统上)。本书后续章节会陆续详细介绍其中的各个组成部分。

(5)Sulong

Sulong子项目是GraalVM为LLVM的中间语言bitcode提供的高性能运行时工具,是基于Truffle框架实现的bitcode解释器。Sulong的名字实际上就是中文“速龙”的拼音,因为Oracle GraalVM的团队组成比较多元,来自不同国家的人们以自己的文化背景为子项目命名。Sulong为所有可以编译到LLVM bitcode的语言(如C、C++等)提供了在JVM中执行的解决方案。

(6)wasm

wasm目录中存放了GraalWasm子项目——一个基于GraalVM实现的WebAssembly引擎,用于解释执行或者编译一个WebAssembly程序。WebAssembly[2](简称wasm)采用一种基于堆栈虚拟机的二进制指令格式,用于将应用程序部署在Web上,由浏览器直接运行,从而获得更高的性能。GraalWasm也是基于Truffle实现的。

从以上子项目的组成可以看到,GraalVM将各种不同的语言汇集于统一的Truffle运行时平台执行,再由GraalVM编译器在运行时通过JIT编译加速执行。因为GraalVM整体使用Java编写,所以理论上这些子项目最终都可以被Substrate VM静态编译,作为动态库so文件嵌入其他项目中,实现更进一步的性能提升,这也是Oracle GraalVM项目组的一大愿景。目前GraalVM编译器已经被Substrate VM静态编译为libgraal.so库文件,然后集成到OpenJDK中取代了传统的C2编译器。

为了便于管理子项目间的内外依赖和编译整个项目,GraalVM的编译系统将这些子项目进一步划分为粒度更小的组件(component)。表3-1给出了GraalVM 20.3版本的组件列表,这个列表会随着版本升级而发生变化。第一列是组件的全名;第二列是组件简称,主要用于GraalVM的编译系统;第三列是组件所在的子项目,这里子项目本身也可以作为组件存在。GraalVM使用的编译系统工具mx可以在组件级别的粒度上,通过各种选项控制具体要编译的成品。

表3-1 GraalVM项目组件列表[3]

040-01

[1]参见https://graalworkshop.github.io/2021/slides/5_GraalVM_at_Facebook.pdf

[2]参见https://webassembly.org/

[3]本项目组件表基于GraalVM20.3.0,在GraalVM项目根目录下搜索grep $'name=\'.*\',\nshort_name=\'.*\" .-rnI可得。