- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
多语言编程
GraalVM 允许用户通过Truffle 语言实现框架(以下简称“Truffle”)编写多语言应用程序,从而在不同语言之间无缝传递值。
Truffle 是一个 Java 库,用于构建编程语言实现,作为自修改抽象语法树的解释器。当使用 Truffle 编写语言解释器时,它将自动使用 Graal 编译器作为该语言的即时编译器。通过访问此框架,例如,Ruby 应用程序可以与 Java 应用程序在同一个 JVM 上运行。此外,基于 JVM 的宿主语言和访客语言可以直接相互操作,并在同一内存空间中来回传递数据。
为了在用 Truffle 实现的语言中提供外部多语言值,开发了所谓的多语言互操作协议。该互操作协议包含一组标准化消息,每种语言都实现并使用这些消息来处理外部多语言值。该协议允许 GraalVM 支持任意语言组合之间的互操作性,而无需它们相互了解。欲了解更多详情,请参阅《多语言运行时中的高性能跨语言互操作性》论文。
本节将介绍如何使用 GraalVM 多语言 API 组合多种语言。
运行多语言应用程序 #
以下示例旨在帮助您开始使用基本的多语言应用程序。选择您的起始语言部分,然后选择目标语言选项卡。
以下示例预计可在 JVM 或本地独立发行版中同样运行。对于使用 Java 作为目标语言并访问除 Java 数组之外的类的本地启动器和本地可执行文件,需要重新编译映像并提供一个反射配置文件。
注意:要启动以 LLVM 作为目标语言的应用程序,请确保预编译下面提供的 polyglot.c 文件。
从 JavaScript / Node.js 开始 #
创建文件 polyglot.js
// BEGIN-SNIPPET
var array = Polyglot.eval("R", "c(1,2,42,4)")
console.log(array[2]);
// END-SNIPPET
// BEGIN-SNIPPET
var array = Polyglot.eval("ruby", "[1,2,42,4]")
console.log(array[2]);
// END-SNIPPET
// BEGIN-SNIPPET
var array = Polyglot.eval("python", "[1,2,42,4]")
console.log(array[2]);
// END-SNIPPET
// BEGIN-SNIPPET
var array = new (Java.type("int[]"))(4);
array[2] = 42;
console.log(array[2])
// END-SNIPPET
// BEGIN-SNIPPET
var cpart = Polyglot.evalFile("llvm", "polyglot");
cpart.main()
// END-SNIPPET
运行
js polyglot.js
42
node polyglot.js
42
起始语言 R #
创建文件 polyglot.R
# BEGIN-SNIPPET
array <- eval.polyglot("js", "[1,2,42,4]")
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
array <- eval.polyglot("ruby", "[1,2,42,4]")
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
array <- eval.polyglot("python", "[1,2,42,4]")
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
array <- new("int[]", 4)
array[3L] <- 42
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
cpart <- eval.polyglot("llvm", path="polyglot")
cpart$main()
# END-SNIPPET
运行
Rscript polyglot.R
[1] 42
起始语言 Ruby #
创建文件 polyglot.rb
# BEGIN-SNIPPET
array = Polyglot.eval('js', '[1,2,42,4]')
puts array[2]
# END-SNIPPET
# BEGIN-SNIPPET
array = Polyglot.eval('R', 'c(1L,2L,42L,4L)')
puts array[2]
# END-SNIPPET
# BEGIN-SNIPPET
array = Polyglot.eval('python', '[1,2,42,4]')
puts array[2]
# END-SNIPPET
# BEGIN-SNIPPET
array = Java.type('int[]').new(4)
array[2] = 42
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
cpart = Polyglot.eval_file('llvm', 'polyglot')
cpart.main()
# END-SNIPPET
运行
ruby polyglot.rb
42
起始语言 Python #
创建文件 polyglot.py
# BEGIN-SNIPPET
import polyglot
array = polyglot.eval(language="js", string="[1,2,42,4]")
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import polyglot
array = polyglot.eval(language="R", string="c(1L,2L,42L,4L)")
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import polyglot
array = polyglot.eval(language="ruby", string="[1,2,42,4]")
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import java
array = java.type("int[]")(4)
array[2] = 42
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import polyglot
cpart = polyglot.eval(language="llvm", path="polyglot")
cpart.main()
# END-SNIPPET
运行
graalpy polyglot.py
42
起始语言 Java #
创建文件 Polyglot.java
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context polyglot = Context.create();
Value array = polyglot.eval("js", "[1,2,42,4]");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context polyglot = Context.newBuilder().
allowAllAccess(true).build();
Value array = polyglot.eval("R", "c(1,2,42,4)");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context polyglot = Context.newBuilder().
allowAllAccess(true).build();
Value array = polyglot.eval("ruby", "[1,2,42,4]");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context context = Context.newBuilder().allowIO(true).build();
Value array = context.eval("python", "[1,2,42,4]");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import java.io.*;
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) throws IOException {
Context polyglot = Context.newBuilder().
allowAllAccess(true).build();
File file = new File("polyglot");
Source source = Source.newBuilder("llvm", file).build();
Value cpart = polyglot.eval(source);
cpart.execute();
}
}
// END-SNIPPET
运行
javac Polyglot.java
java Polyglot
42
起始语言 C #
创建文件 polyglot.c
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("js", "[1,2,42,4]");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("R", "c(1,2,42,4)");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("ruby", "[1,2,42,4]");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("python", "[1,2,42,4]");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *arrayType = polyglot_java_type("int[]");
void *array = polyglot_new_instance(arrayType, 4);
polyglot_set_array_element(array, 2, 42);
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
示例 C 代码必须使用诸如 clang
之类的 LLVM 前端编译为 LLVM 位码。用户可以使用 GraalVM LLVM 运行时附带的预构建LLVM 工具链中的 clang
。
export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)
运行
$LLVM_TOOLCHAIN/clang polyglot.c -lgraalvm-llvm -o polyglot
lli polyglot
42
多语言选项 #
您可以配置语言引擎以获得更好的吞吐量或启动性能。
--engine.Mode=default
配置引擎的执行模式。执行模式会自动调整多语言引擎以侧重于延迟或吞吐量。throughput
(吞吐量)模式会收集最大量的性能分析信息,并使用最大数量的优化进行编译。此模式会导致应用程序启动较慢但吞吐量更高。除非另有指定,否则此模式使用community
或enterprise
编译器配置。default
(默认)模式使用均衡的引擎配置。除非另有指定,否则此模式使用community
或enterprise
编译器配置。latency
(延迟)模式仅收集最少的性能分析信息,并以最快的速度编译生成非最优代码。此模式会导致应用程序启动更快但吞吐量不那么理想。除非另有指定,否则此模式使用economy
编译器配置。
为语言启动器传递选项 #
每个语言启动器都已扩展了一组所谓的多语言选项。多语言选项允许任何语言启动器的用户访问 GraalVM 支持的其他语言(使用 Truffle 语言实现框架实现)的选项。格式为:--<languageID>.<property>=<value>
。例如,R
启动器也支持 --js.atomics=true
JavaScript 选项。
languageID
的允许值是
js
:JavaScript 选项python
:Python 选项r
:R 选项ruby
:Ruby 选项llvm
:LLVM 选项
使用 --help:languages
查找可用选项。
多语言工具的选项以相同的方式工作,格式如下:--<toolID>.<property>=<value>
。
<toolID>
的允许值是
inspect
:允许使用 Chrome DevTools 进行调试cpusampler
:收集 CPU 使用情况数据cputracer
:捕获 CPU 使用情况的跟踪信息memtracer
:捕获内存使用情况的跟踪信息
使用 --help:tools
查找可用选项。
以编程方式传递选项 #
选项也可以使用 Java 多语言 API 以编程方式传递。
创建一个名为 OptionsTest.java
的文件
import org.graalvm.polyglot.*;
class OptionsTest {
public static void main(String[] args) {
Context polyglot = Context.newBuilder()
.allowExperimentalOptions(true)
.option("js.shared-array-buffer", "true")
.build();
// the use of shared array buffer requires the 'js.shared-array-buffer' option to be 'true'
polyglot.eval("js", "new SharedArrayBuffer(1024)");
}
}
运行
javac OptionsTest.java
java OptionsTest
注意:工具选项也可以用相同的方式传递。上下文创建后,选项不能修改。
使用 JVM 参数传递选项 #
每个多语言选项也可以作为 Java 系统属性传递。每个可用选项都转换为带有 polyglot.
前缀的系统属性。例如,-Dpolyglot.js.strict=true
设置了在 JVM 中运行的所有 JavaScript 代码的严格解释的默认值。以编程方式设置的选项优先于 Java 系统属性。对于语言,可以使用以下格式:-Dpolyglot.<languageID>.<property>=<value>
,对于工具,则为:-Dpolyglot.<toolID>.<property>=<value>
。
创建一个名为 SystemPropertiesTest.java
的文件
import org.graalvm.polyglot.*;
class SystemPropertiesTest {
public static void main(String[] args) {
Context polyglot = Context.newBuilder()
.allowExperimentalOptions(true)
.build();
// the use of shared array buffer requires the 'js.shared-array-buffer' option to be 'true'
polyglot.eval("js", "new SharedArrayBuffer(1024)");
}
}
运行
javac SystemPropertiesTest.java
java -Dpolyglot.js.strict=true SystemPropertiesTest
注意:系统属性在多语言上下文创建时读取一次。随后的更改无效。
相关文档 #
- 从《嵌入语言》文档中了解有关访客语言和 Java 宿主语言互操作性的更多信息