Oracle 发布多语种虚拟机平台 GraalVM 1.0

  • Ben Evans
  • 金灵杰

2018 年 5 月 6 日

话题:JavaRubyJavaScriptPythonDevOps语言 & 开发架构

看新闻很累?看技术新闻更累?试试下载 InfoQ 手机客户端,每天上下班路上听新闻,有趣还有料!

Oracle 发布了多语种虚拟机平台GraalVM 的 1.0 版本。初始发布版包括运行 Java 和 JVM 语言(通过字节码)的能力,对 JavaScript 和 Node.JS 的全面支持,以及对 Ruby、Python、R 语言和 LLVM 字节码的测试性支持。

整个平台包含多个组件:

整个平台的目的是提供一个可以嵌入到另一个执行容器中的多语言执行环境,不论这个执行容器是 OpenJDK 容器还是诸如 Oracle 或者 MySQL 数据库之类的均可。

InfoQ 与 Oracle 实验室研究总监 Thomas Wuerthinger 就 GraalVM 进行了深入访谈。

InfoQ:您能介绍一下 GraalVM 的历史吗?它哪里

Thomas Wuerthinger:这个项目最初的想法是编译器不应该对编程语言语义有特殊支持。究其原因是 Oracle 内部对包括 JavaScript、R 和 Ruby 在内的多种编程语言都有所涉猎。

我们觉得以下两个核心领域可以通过研究进行突破:

  • 软件行业的虚拟机架构师普遍认为,如果想获得最优的性能,某种语言的虚拟机需要结合该语言的语义针对性设计。
  • 从 Java7 开始,通过在 JVM 规范中增加 invokedynamic 字节码指令,Java 架构支持了多种语言。GraalVM 团队觉得由于 Java 字节码已经内置了太多 Java 语义,100% 的兼容实现长期来看会有更好的性能。

同时我们还有一个目标是构建一个有竞争力的完备的多语言引擎,因此我们开始和一些大学开始合作开展支持其他语言的工作。

普渡大学的一个团队做了 R 语言的一个实现,加州大学欧文分校的一个团队在 2012 年到 2014 年完成了 Python 语言的一个实现。一个实习生将 Ruby 语言实现作为他的论文项目,它有很多新奇有趣的特性,我们认为它很好地证明了我们的多语言编译器是完全通用的。随后,我们将这些语言的项目带到了 Oracle 实验室以为这些语言编写工业质量的实现。

随着 GraalVM 1.0 的发布,我们认为我们已经证明了拥有高性能的多语言虚拟机是可能的,并且实现这个目标的最佳方式不是通过类似 Java 和微软 CLR 那样带有语言特性的字节码。

InfoQ:组成 GraalVM 的核心技术是什么?这些技术将如何帮助开发人员?

Wuerthinger:GraalVM 的核心技术称为“部分评估”,它是一项将解释器自动转换成编译器的技术。语言开发者能够仅仅专注于构建一个抽象语法树(Abstract Syntax Tree,AST)解释器,而无需编写代码生成器和其他诸如垃圾回收器等低级运行时功能。

我们内置于 GraalVM 的一项核心技术是逻辑 / 物理数据分离。这使得任何内存数据布局都能够表现为一个本地语言对象。大部分虚拟机对于对象都有一个特定的布局(对象头通常称为“盒子”),因此为了获取对象在编程语言中的语义,对象内存结构必须有相同的布局。

这意味着在大部分虚拟机中,用户必须为一个“对象”分配内存和复制数据。

在 Graal 中,不同语言互操作可以实现零开销,因为 JavaScript 对象表现的行为可以是 JavaScript 对象,也可以是 R 对象,而无需额外的对象创建和数据复制。

该特性也允许例如以特定二进制格式暴露高级语言对象数据,而无需实际创建这些高级语言对象。

另一项核心技术是 LLVM 解释器。我们实现了一个针对 LLVM 字节码的 GraalVM 解释器,该字节码是 LLVM 编译器的输出,因此可以完美支持生成原生二进制可执行文件的语言(例如 C++、FORTRAN、Rust 等等)。

实现语言支持的一个主要问题是,这些语言通常有使用本地字节码实现的库文件(通常是 C/C++),并且它们会使用暴露语言内部的 API。因此如果希望兼容一个语言系统,我们必须支持本地库。

像 JRuby 和 Nashorn 那样的实现方式其中一个问题就是,它们无法支持包含本地库的模块,这会导致兼容性问题。

GraalVM 最后增加的独立模块称为 SubstrateVM。它的做法是将虚拟机在混合和匹配的基础上变得可嵌入。类似 Java Hotspot 这样的常规语言虚拟机期望能够控制所有东西:哪个线程能够被调度,内存分配器如何工作,访问操作系统等等。

因此,我们需要一种允许底层系统提供这些服务的技术层,而 SubstrateVM 通过支持动态 JIT 编译器(如垃圾回收器)所需要的内容来弥补这一缺失。例如,当我们运行在一个数据库中时,数据库希望能够控制内存管理、线程分配等。

SubstrateVM 是一个“极薄”的虚拟机,能够嵌入到类似数据库这样的系统中。因此如果用户需要在数据库中运行 Graal 编译器,数据库可以指定最大堆内存和不是通过 Java 虚拟机机制,通知数据库可以管理代码构件而不是 JVM 类加载器。

SubstrateVM 实现这一功能的最重要方式是像 Graal 一样对 Java 代码进行提前编译(ahead-of-time compilation)而成为本地二进制代码,当然需要假设所有依赖自包含封闭世界假定(closed world assumption)(所有需要使用的类在编译期都需要明确)。因此,当用户将 Graal 编译器部署到数据库的时候,需要使用 Graal 将 GraalVM 语言编译成本地共享库,并且它们不必在运行时编译。

InfoQ:什么是 Truffle?为什么需要它?

Wuerthinger:Truffle 是构建一个新的 GraalVM 语言时所依赖的 GraalVM 解释器 API。解释器的工作首先是将源码解析成所谓的抽象语法树(Abstract Syntax Tree,AST)。例如,如果我们的代码是:c = a + b,那么 AST 将会包含 2 个节点,一个是加操作,它包括两个子节点 a 和 b,第二个节点是赋值操作,包含子节点 c 和加操作节点。

解释器内部实现,例如 AST 相关操作需要继承 Truffle 接口,如果这样做了 Graal 编译器将会观察语言解释器活动以学习语言语义,因此可以生成针对这种语言的编译器。

InfoQ:GraalVM 的最令人瞩目的组件之一是 Graal 编译器,它是一个名为“Graal”、基于 Java 的 JIT 编译器。您能介绍一下 Graal 希望能够改进当前的 C2 编译器(目前 OpenJDK 中使用的由 C++ 编写的 JIT 编译器)遇到的难点吗?另外,Graal 未来有希望替代 C2 称为 OpenJDK 主线的默认编译器吗?

Wuerthinger:Graal 编译器在 Java 10 中是一个实验性的编译器,Oracle 对于它将来能够成为默认编译器是乐观的。

类似 Graal 这样的编译器最大的优势是它首先编译其自身(因为它由 Java 编写),这使得可以直接在高级语言层面编写 Graal,而不用像 C2 那样直接操作 AST。举例来说,对于 Graal 来说能够更容易的进行积极内联和对象逃逸分析,这使得程序能够自动的移除更多的对象分配。总的来说,程序写的约抽象,Graal 编译器能够做的更多,因此我们会看见 Graal 对于 Java 8 或者 Scala 中支持的流(Stream)和 Lambda 等功能会有更大的性能提升。

InfoQ:Oracle 正在将 GraalVM 作为多语言虚拟机进行大力推广。其 1.0 版本发布的内容和 Java 10 中包含的部分内容有什么区别?对于类似 JRuby 开发者来说,应该选择哪个?GraalVM 应该被视作是一个独立的项目,还是 OpenJDK 的一部分?未来 Graal 的发布节奏会与 Java 发布节奏保持一致,还是会独立进行?

Wuerthinger:GraalVM 和 Java 是 100% 独立的项目,它们都拥有大量的开源存在和独立的发布节奏,并且它们都同时从彼此项目中获取各种技术。Java 10 从 GraalVM 中获取了编译器(作为实验性选择),而 GraalVM 将 Java 8 作为其支持的语言之一。GraalVM 支持许多语言,不仅仅是 Java,因此我们也使用了来自 Node.js、JRuby 的技术,来自 GNU R 语言的实现等内容。

和大多数组织中的技术生产者 / 消费者关系类似,消费者会从其他提供者处等待新技术的成熟,然后使用它。类似的,Java 平台组织(Java Platform Group)在引入新技术的时候也是保守的,他们需要确认这项技术不会破坏现存的部署,以及一系列向下和向上兼容性考虑,所以会有一些滞后。

InfoQ:IBM 实现的 OpenJ9 运行时环境使用了和 GraalVM 不同的实现方式。您能解释一下您了解到的和 OpenJ9/OMR 主要区别是什么,GraalVM 有什么独到之处?

Wuerthinger:OpenJ9 试图通过增加一些限制性的 JIT 编译功能来加速已经存在的单一语言运行时环境(例如 Ruby 的 MRI 解释器)。他们这种实现方式的优点是适配和兼容已经存在的运行时环境比较方便。主要的劣势是提升比较有限。

首先,与在 GraalVM 上运行的 Ruby 数量级相比,他们的 Ruby 原型的性能优势非常小。主要原因是 GraalVM 可以进行更多的分析和推测优化,而 OMR 保留了现有解释器的数据结构。

其次,他们需要开发者编写一个代码生成器,并且无法通过部分评估自动从解释器中得到编译后的代码。

第三,没有内置的物理 / 逻辑数据分离。

这点直接导致了最终到的第四点:通过他们的实现方式没有能力实现高效的语言互操作性。

OMR 可以对类似 Ruby MRI 这样尚未进行很好优化的现存单语言运行时提供微小的性能提升。GraalVM 使用了完全不同的方式对这些语言有了数量级的改进,并且对于任何支持的语言有零开销的互操作性。

InfoQ:对于 Graal 更广泛的社区参与情况如何?Twitter 是有记录的早期使用者和在生产环境中使用运行 Graal 的公司,其他还有已经使用的场景吗?您与非 Oracle 开发人员一起工作的经验是什么?Graal 未来的开发模式会是如何的?它是否会使用和 OpenJDK 类似的模型?

Wuerthinger:OpenJDK 的任务和 GraalVM 的完全不同。

OpenJDK 对于开发者来说水很深,因为它总是创建一个新的语言特性或者新的库。开发者不得不跟随 OpenJDK 改变而修改代码,而这些改动的目的是让社区对其价值达成共识。OpenJDK 社区非常庞大,有许多不同的观点,他们的流程主要用于帮助社区达成共识。

GraalVM 的目的是让已经存在的代码和库能够更高效、在更多平台的运行,同时拥有更多的互操作性,并且不修改语言语义(对大部分语言来说)。对于开发者接口的唯一主要部分是互操作 API,它能够允许在其他运行时环境中调用外部语言函数,我们尽可能保持对外接口的稳定。

GraalVM 支持语言的社区都有自己的发展规划。

和 OpenJDK 一样,GraalVM 有许多来自厂商的巨大贡献,其中 Red Hat、Intel 和 AMD 贡献最大。Red Hat 在不久前在我们没有多大的投入之下为 GraalVM 构建了一个 ARM 后端,这对于我们来说非常有用。你可以已经注意到 Oracle 最近投资了 ARM,因此我们会更加关注该生态系统。

这些厂商认为 GraalVM 将获得足够的认可以确保投资回报,他们的兴趣主要不是自身使用。正如你指出的,Twitter 也对 GraalVM 作出了贡献,因为他们正在使用。其他还有一些主要的科技厂商在使用 GraalVM 但是没有像 Twitter 那样公布出来,另外 Oracle 内部也有使用者。

InfoQ:Oracle 通常很少谈论未来路线图。您能否告诉我们在未来 18 个月内 GraalVM 的发展轨迹可能会带给我们什么?

Wuerthinger:好吧,GraalVM 团队属于 Oracle 实验室,因此我们会有相反的问题,就是我们有许多公开研究性的项目,它们或多或少会超出我们在学术会议论上发表论文的范围。Oracle 实验室有一批研究生合作者,希望通过使用 GraalVM 尝试新的创意。

我们有一个学生正在考虑将 GraalVM 链接到 Chromium 中,这样我们可以在浏览器中使用任何语言,而不仅仅是 JavaScript。但是我认为很明显这不可能称为 Oracle 的一个产品而发布。你可以查阅 GraalVM 相关学术论文来了解这些疯狂的想法。我们已经完成了对其他一些语言社区的调查,但是否支持得根据社区的兴趣。

关于路线图我能够告诉你的是,我们正在致力于支持更多的平台。初始发布的版本只能运行在 Linux/X86 和 Mac/X86 平台上,以降低“发布压力”。我们正在积极致力于 Windows 平台和 ARM 平台的支持(和 Red Hat 一起)。对于目前被标记为“实验性”的语言来说,例如 R、Ruby 和 Python,还有许多稳定性和完善性的工作要做。

就整合而言,我们将通过多种方式将 GraalVM 更深入的集成到 MySQL 和 Oracle RDBMS 中,这些会在未来 18 个月中看见。但是 18 个月后 GraalVM 会是什么样子,这非常依赖整个社区的反馈和贡献。

查看英文原文:Oracle Releases GraalVM 1.0, a Polyglot Virtual Machine and Platform


感谢冬雨对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

JavaRubyJavaScriptPythonDevOps语言 & 开发架构