GraalVM 21.0 引入了一个新的 JVM 实现 Java on Truffle,这是一个使用 Java 编写的 Java 虚拟机,从而提供了一种在 GraalVM 上运行 Java 代码的新方式。
到目前为止,GraalVM 已经提供了两种运行 Java 程序的方式:组合使用 Java HotSpot VM 和 GraalVM JIT(just-in-time)编译器,以及使用 GraalVM Native Image 编译成原生可执行文件。
如今,我们很高兴地宣布一种在 GraalVM 运行 Java 的新方法。GraalVM 21.0 引入了一个新的可安装的组件,叫做espresso
,它提供了一个使用 Java 编写的 JVM 实现。
Java on Truffle 在 Java 架构中的位置
Espresso 使得通过 Truffle 框架运行 Java 代码成为可能,从而将 Java 提升到与 GraalVM 所支持的其他语言相同的级别。
尝试运行
尝试 Java on Truffle 是非常简单直接的。它可以使用 gu 命令,作为一个组件安装到基础 GraalVM 发行版中。
gu install espresso gu install -L espresso-installable.jar # if downloaded manually
安装之后,要在 Java on Truffle 运行你最喜欢的应用,只需要为java
命令传递-truffle
参数即可。
java -truffle -jar myApp.jar
复制代码
请下载 Java on Truffle 并尝试一下。这里有一些带有指令的示例应用,阐述了 Java on Truffle 的特殊功能。
注意,当前 Java on Truffle 的性能并不代表它在不远的将来的能力。目前它的峰值性能要比常规 JIT 模式下运行相同的代码慢好几倍。另外,预热也还没有进行优化。在这个初始发布版本中,我们完全专注于功能、兼容性,并让 Java on Truffle 开源,使其能够在更广大的社区中得到应用。
预计在我们即将发布的每个 21.x 版本中,性能(包括预热性能和峰值性能)将会得到迅速提升。
现在,我们更加详细地看一下 Java on Truffle 是什么并探索一些它提供帮助的典型用例,尝试将该项目放到更大的 GraalVM 和 Java 生态系统中。
Java on Truffle
Java on Truffle 是一个 JVM 实现,它使用了 Truffle 语言实现框架。它提供了 Java 虚拟机所有的核心组件:
字节码解释器
字节码校验器
单程(Single-pass)的 class 文件解析器
简单的对象模型
使用 Java 语言编写的 Java 原生接口(Java Native Interface,JNI)实现
使用 Java 实现的 Java 虚拟机
Java Debug Wire Protocol(JDWP)
这个 JVM 实现有很重要的一点就是它是使用 Java 编写的。Java on Truffle 是基于 Java 的 Java(Java on Java)。自托管是 Java 虚拟机研究和开发领域的圣杯。
它所能做的就是运行 Java 程序。当然,它也可以运行用其他 JVM 语言编写的程序。从上面的列表我们可以看出,它还支持调试协议,所以我们也可以使用它调试 Java 应用。
Java on Truffle 能够用于基于 Java 8 和 Java 11 的 GraalVM 发行版,所以从技术上来讲,你可以使用它来替代自己所选择的 JVM。Java on Truffle 目前是实验性的,速度还不是很快,所以现在不建议将其用于生产环境的工作负载,但是我们可以探索一下使用 Java on Truffle 运行应用程序都能实现哪些特性。
基于 Java 的 Java
正如我们在前文所描述的,Java on Truffle 是使用 Java 实现的。它是一个虚拟机实现,所以为了实际运行 Java 代码,它需要访问类库以及 JDK 提供的本地库和方法。Java on Truffle 重用了 GraalVM 分发版的 JAR 和本地库。
因为它是使用 Java 实现的并且能够运行 Java,这使得 Java on Truffle 具有了非常有意思的特性:它可以运行自己!事实上, Java on Truffle 是一个元循环(metacircular)的虚拟机,它可以嵌套运行自己好几层(不过每次都会越来越慢)。
作为一个 Java 程序,它有很多优势。其中之一就是能够使用原生镜像编译成原生的可执行文件,在后面的章节中我们将会讨论一个很有意思的用例。
另外一个优势就是,对于 Java 开发人员来说,代码非常友好和熟悉,易于理解。读者可以考虑去 GitHub 仓库查看它的源码。你的日常工具适用于它,你的 IDE 支持它,你可以像探索其他 Java 依赖那样去探索它的代码。这种透明度和熟悉度能够让 Java on Truffle 更加高效地应对变化。
将 Java 11 嵌入到 Java 8 中
Java on Truffle 实际就是一个 JVM,但它也是一个 Java 程序,这意味着你可以在其他 Java 程序中运行它。这就为将应用程序中的不同组件进行分割开辟了一个很有趣的途径。举例来说,如果你将 Java on Truffle 指向一个 JDK 11,那么它就能运行 Java 11。如果访问 Java 8,那么它就会成为 Java 8。如果你同时拥有这两个发行版的话,那么你就可以在 Java 8 的环境中运行 Java on Truffle,并使用它来运行 Java 11 字节码,反之亦然。如果有个库只能用于 Java 8,那么你可以迁移至更新的基础 JDK,并且通过一些编程方面的努力让这个特定的库依然能够运行,这需要建立一种互操作性,在相同的 Java 进程中建立兼容的 JDK 8 环境。
混合 AOT 和 JIT
因为 Java on Truffle、Truffle、GraalVM 编译器和运行 Java on Truffle 的所有其他必要的组件都是使用 Java 编写的,所以我们能够构建一个包含运行 Java on Truffle 基础设施的原生镜像可执行文件。
这意味着,我们可以基于 Java 应用,将一个 JVM 构建到它的里面,然后要么在 JVM 上运行该应用,要么以原生镜像的方式运行该应用。需要注意,在后面的情况中,Java on Truffle 实际上可以执行任意的 Java 代码,这些代码不一定需要在构建的时候就得知道。
没错,Java on Truffle 可以将 JIT 编译器和动态 Java 运行时用到一个提前编译的二进制文件中。
Java on Truffle 允许为使用原生镜像构建的应用添加动态语言的特性
我们准备了一个样例应用来阐述这个理念。这是一个 JShell 实现的样例,它接受一个常规的 JShell 应用,该应用包含两个组成部分:前端的 CLI 应用和后端的计算引擎,我们将后者替换成了 Java on Truffle 实现。
实际上,它非常整洁地重用了原来实现中的所有类,只需加载它们即可。因此,示例应用程序的原始部分是连接托管 Java 部分和 Java on Truffle 部分的“胶水”代码。
这个示例可以被编译成原生可执行文件,以得到一个二进制文件,由于原生可执行文件的性能特征,它的启动速度比一般的 JShell 要快,而且仍然可以执行我们扔给它的 Java 代码。
下面是 Java on Truffle 实现的 JShell 加载并启动俄罗斯方块游戏的截图。
混合 AOT 和 JIT 对于那些不能利用原生镜像性能改进的应用程序来说是一个非常棒的可选方案,因为这些应用的功能依赖于动态代码,而动态代码不容易与原生镜像一起工作。
高级的类重定义
Java on Truffle 有另外一项很酷的特性,比 HotSpot 更强大,这就是增强的热替换(Hot Swap)能力:在调试会话中,运行时改变类。
GraalVM 21.0 支持如下的变化:
添加和移除方法
添加和移除构造器
从接口中添加和移除方法
改变方法的访问修饰符
改变构造器的访问修饰符
Lambda 的变化
添加新的匿名内部类
移除匿名内部类
未来将会使热替换更强大的是支持对类字段的变化。该特性正在进行中,会在未来的释放版本中添加进来。
在 IntelliJ IDEA 调试器中重新加载修改的类
这个搭建过程与 HotSpot 相同:启动调试器、修改代码、重新编译类、在 IDE 调试器中点击“Reload the classes”并使用新代码恢复程序运行,下一次变更的代码就会生效。
GraalVM 生态系统的支持
对于 GraalVM 语言从 Truffle 框架中获取的开发人员工具支持来说,Java on Truffle 会使它们从中收益。
例如,你可以在运行应用的时候,使用某些java -truffle --cpusampler
,这样的话就能让采样剖析器(profiler)在代码上运行。我们还可以启用跟踪剖析器或内存跟踪器,从而告诉我们代码的哪个部分产生了更大的内存压力。
生态系统的另一个方面是支持的语言。Java on Truffle 允许我们创建多语言程序,不同的组件用不同的语言编写。关于如何加载用其他语言编写的代码、在不同语言之间导出和导入对象等细节,超出了本文的范围,但细节可以从文档中找到。
下一步的计划
GraalVM 21.0 是 Java on Truffle 的初始版本。现在它是一个实验性的组件,在接下来的版本中我们计划对它进行重大改善。
它有很多需要改进的地方,从支持 javaagents,到与其他语言的互操作协议方面提供更好的实现,重大的性能改进等等。
我们将致力于这些和其他方面的改进,并且会非常乐意听到任何反馈、特性请求、潜在的用例、发现的问题和当前版本的不足之处。你可以通过Slack、GitHub或Twitter分享你的反馈。要想开始学习它,请前往 graalvm.org/java-on-truffle。
Java on Truffle 是一种全新的、非常令人兴奋的运行 Java 代码的方式。请了解并考虑一下它所带来的可能性。