GraalVM有不少不一样的部分,所以,若是您之前听过这个名字,或者甚至看过咱们的一些谈话,那么确定能够作的事情您还不知道。在本文中,咱们将列出GraalVM的一些不一样功能,并向您展现它们能够为您作些什么。css
您能够复制的一切,我展现这篇文章GraalVM 19.3.0,这是能够从graalvm.org下载。我在macOS上使用企业版,该版本可在此处免费评估,但说明也可在Linux上使用。他们中的大多数人还将与社区版一块儿使用。
在阅读的同时,继续并运行这些程序!我在GraalVM上运行的代码能够从github.com/chrisseaton/graalvm-ten-things/中复制。
设定
我已经从www.oracle.com/downloads/graalvm-downloads.html下载了适用于macOS的基于JDK8的GraalVM Enterprise Edition ,并将其中的程序放到了个人$PATH。默认状况下,这为我提供了Java和JavaScript语言。html
$ git clone https://github.com/chrisseaton/graalvm-ten-things.git $ cd foo $ tar -zxf graalvm-ee-java8-darwin-amd64-19.3.0.tar.gz # or graalvm-ee-java8-linux-amd64-19.3.0.tar.gz on Linux $ export PATH=graalvm-ee-java8-19.3.0/Contents/Home/bin:$PATH # or PATH=graalvm-ee-java8-19.3.0/bin:$PATH on Linux
GraalVM随附了JavaScript,并具备一个名为的软件包管理器gu,可以让您安装其余语言。我已经安装了Ruby,Python和R语言。我还安装了该native-image工具。这些均可以从GitHub下载。前端
$ gu install native-image $ gu install ruby $ gu install python $ gu install R
如今,当您运行时java,js您将得到那些运行时的GraalVM版本。java
$ java -version java version "1.8.0_231" Java(TM) SE Runtime Environment (build 1.8.0_231-b11) Java HotSpot(TM) 64-Bit GraalVM EE 19.3.0 (build 25.231-b11-jvmci-19.3-b05, mixed mode) $ js --version GraalVM JavaScript (GraalVM EE Native 19.3.0)
GraalVM中的Graal名称来自GraalVM编译器。GraalVM是一个能够所有处理的编译器,这意味着它是做为库编写的编译器的单个实现,能够用于许多不一样的事情。例如,咱们使用GraalVM编译器提早编译和及时编译,以编译多种编程语言和多种体系结构。
使用GraalVM的一种简单方法是将其用做Java JIT编译器。
咱们将使用此示例程序,该程序为您提供文档中的前十个单词。它使用了现代Java语言功能,例如流和收集器。node
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; public class TopTen { public static void main(String[] args) { Arrays.stream(args) .flatMap(TopTen::fileLines) .flatMap(line -> Arrays.stream(line.split("\\b"))) .map(word -> word.replaceAll("[^a-zA-Z]", "")) .filter(word -> word.length() > 0) .map(word -> word.toLowerCase()) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet().stream() .sorted((a, b) -> -a.getValue().compareTo(b.getValue())) .limit(10) .forEach(e -> System.out.format("%s = %d%n", e.getKey(), e.getValue())); } private static Stream<String> fileLines(String path) { try { return Files.lines(Paths.get(path)); } catch (IOException e) { throw new RuntimeException(e); } } }
GraalVM包含一个javac编译器,可是对于本演示而言,它与标准编译器没有什么不一样,所以,javac若是须要,您可使用系统。python
$ javac TopTen.java
若是咱们运行javaGraalVM中包含的命令,咱们将自动使用Graal JIT编译器-不须要额外的配置。我将使用该time命令来获取从头至尾运行整个程序所需的实际时间,而不是设置复杂的微基准测试,而我将使用大量输入,以便咱们在这里或那里大约要花几秒钟的时间。该large.txt文件为150 MB。linux
$ make large.txt $ time java TopTen large.txt sed = 502701 ut = 392657 in = 377651 et = 352641 id = 317627 eu = 317627 eget = 302621 vel = 300120 a = 287615 sit = 282613 real 0m12.950s user 0m17.827s sys 0m0.622s
GraalVM用Java编写,而不是像大多数其余Java的JIT编译器那样用C ++编写。咱们认为,这可使咱们比现有的编译器更快地进行改进,它具备强大的新优化功能,例如用于HotSpot的标准JIT编译器中没法提供的部分转义分析。这可使您的Java程序运行明显更快。
要在没有GraalVM JIT编译器进行比较的状况下运行,我可使用flag -XX:-UseJVMCICompiler。JVMCI是GraalVM和JVM之间的接口。您也能够将其与标准JVM进行比较。git
$ time java -XX:-UseJVMCICompiler TopTen large.txt sed = 502701 ut = 392657 in = 377651 et = 352641 id = 317627 eu = 317627 eget = 302621 vel = 300120 a = 287615 sit = 282613 real 0m19.602s user 0m20.357s sys 0m0.498s
这代表GraalVM在使用标准HotSpot编译器运行Java程序所需的时间大约是挂钟时间的三分之二。在咱们习惯将单位数百分比的性能提高视为显着的领域中,这是一个很大的交易。
若是使用社区版,您仍然能够得到比HotSpot更好的结果,可是它不如企业版那么好。
Twitter是当今在生产中使用GraalVM的一家公司,他们说,对于他们而言,它在节省的实际资金方面得到了回报。Twitter正在使用GraalVM运行Scala应用程序— GraalVM在JVM字节码级别上工做,所以可用于任何JVM语言。
这是使用GraalVM的第一种方法-只是做为现有Java应用程序的嵌入式更好的JIT编译器。github
Java平台对于长时间运行的进程和最高的性能特别强大,可是短时间运行的进程可能会遭受更长的启动时间和相对较高的内存使用率。
例如,若是咱们以较小的输入(大约1 KB而不是150 MB)运行相同的应用程序,那么运行这么小的文件彷佛花费了不合理的长时间,而且内存很大,为70 MB。 。咱们-l用来打印所用的内存以及所用的时间。sql
$ make small.txt $ /usr/bin/time -l java TopTen small.txt # -v on Linux instead of -l sed = 6 sit = 6 amet = 6 mauris = 3 volutpat = 3 vitae = 3 dolor = 3 libero = 3 tempor = 2 suscipit = 2 0.17 real 0.28 user 0.04 sys 70737920 maximum resident set size ...
GraalVM为咱们提供了解决此问题的工具。咱们说过GraalVM就像一个编译器库,能够用许多不一样的方式使用。其中之一是提早编译为本地可执行映像,而不是在运行时即时编译。这相似于常规编译器的gcc工做方式。
$ native-image --no-server --no-fallback TopTen [topten:37970] classlist: 1,801.57 ms [topten:37970] (cap): 1,289.45 ms [topten:37970] setup: 3,087.67 ms [topten:37970] (typeflow): 6,704.85 ms [topten:37970] (objects): 6,448.88 ms [topten:37970] (features): 820.90 ms [topten:37970] analysis: 14,271.88 ms [topten:37970] (clinit): 257.25 ms [topten:37970] universe: 766.11 ms [topten:37970] (parse): 1,365.29 ms [topten:37970] (inline): 3,829.55 ms [topten:37970] (compile): 34,674.51 ms [topten:37970] compile: 41,412.71 ms [topten:37970] image: 2,741.41 ms [topten:37970] write: 619.13 ms [topten:37970] [total]: 64,891.52 ms
此命令将生成一个名为的本机可执行文件topten。该可执行文件不是JVM的启动器,它没有连接到JVM,也没有以任何方式捆绑JVM。native-image确实能够编译您的Java代码以及您使用的全部Java库,一直到简单的机器代码。对于运行时组件(如垃圾收集器),咱们正在运行本身的新VM,称为基板虚拟机,就像GraalVM同样,也是用Java编写的。
若是咱们查看topten使用的库,您会发现它们只是标准系统库。咱们也能够仅将这一个文件移动到从未安装过JVM的系统上,而后在该系统上运行以确认它不使用JVM或任何其余文件。它也很是小-该可执行文件小于8 MB。
$ otool -L topten # ldd topten on Linux topten: libSystem.B.dylib (current version 1252.250.1) CoreFoundation (current version 1575.12.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11) $ du -h topten 7.5M topten
若是运行可执行文件,咱们能够看到它比在JVM上运行相同程序的启动速度快大约一个数量级,而且使用的内存少大约一个数量级。它是如此之快,以致于您没有注意到在命令行上使用它所花费的时间—当您使用JVM运行短运行命令时,您不会感受到老是会获得暂停。
$ /usr/bin/time -l ./topten small.txt sed = 6 sit = 6 amet = 6 mauris = 3 volutpat = 3 vitae = 3 dolor = 3 libero = 3 tempor = 2 suscipit = 2 0.02 real 0.00 user 0.00 sys 3158016 maximum resident set size ...
该native-image 工具备一些限制,例如在编译过程当中必须使用全部类,还存在一些与反射有关的限制。与基本编译相比,它还有一些其余优势,由于静态初始值设定项在编译期间运行,所以您能够减小每次加载应用程序时所作的工做。
这是您可使用GraalVM的第二种方法-一种以低占用空间和快速启动的方式分发和运行现有Java程序的方法。它还使您摆脱了配置问题,例如在运行时找到正确的jar文件,并容许您拥有较小的Docker映像。
除了Java以外,GraalVM还包括JavaScript,Ruby,R和Python的新实现。这些都是使用称为Truffle的新语言实现框架编写的,该框架使实现简单而高性能的语言解释器成为可能。当您使用Truffle编写语言解释器时,Truffle会自动表明您使用GraalVM为您的语言提供JIT编译器。所以GraalVM不只是Java的JIT编译器和提早的本机编译器,并且还能够是JavaScript,Ruby,R和Python的JIT编译器。
GraalVM中的语言旨在替代现有语言。例如,咱们能够安装一个Node.js模块:
$ npm install color ... + color@3.1.1 added 6 packages from 6 contributors and audited 7 packages in 6.931s
咱们可使用该模块编写一个小程序color.js,将RGB HTML颜色转换为HSL:
var Color = require('color'); process.argv.slice(2).forEach(function (val) { console.log(Color(val).hsl().string()); });
而后,咱们能够按照一般的方式运行它:
$ node color.js '#42aaf4' hsl(204.89999999999998, 89%, 60.8%)
GraalVM中的语言能够协同工做-有一个API,可以让您从一种语言运行另外一种语言的代码。这样,您就能够编写多语种程序-用多种语言编写的程序。
您可能想这样作,是由于您想用一种语言编写大多数应用程序,可是您想使用另外一种语言的生态系统中的一个库。例如,假设咱们想编写将Node.js中的CSS颜色名称转换为十六进制的应用程序,可是咱们想使用Ruby颜色库进行转换。
var express = require('express'); var app = express(); color_rgb = Polyglot.eval('ruby', ` require 'color' Color::RGB `); app.get('/css/:name', function (req, res) { color = color_rgb.by_name(req.params.name).html() res.send('<h1 style="color: ' + color + '" >' + color + '</h1>'); }); app.listen(8080, function () { console.log('serving at http://localhost:8080') });
咱们指定了一些Ruby代码以字符串形式运行,可是请注意,咱们在其中没有作不少事情-咱们只须要这些库,而后返回一个Ruby对象。从Ruby使用这个对象的方法一般是这样说的Color::RGB.by_name(name).html。若是您color_rgb进一步研究JavaScript的用法,您会发现实际上咱们是从JavaScript调用这些方法的,即便它们是Ruby对象和方法,而且将它们传递给JavaScript字符串,而后将结果链接起来,是Ruby字符串,还有其余JavaScript字符串。
咱们将同时安装Ruby和JavaScript依赖项。
$ gem install color Fetching: color-1.8.gem (100%) Successfully installed color-1.8 1 gem installed $ npm install express + express@4.17.0 added 50 packages from 37 contributors and audited 143 packages in 22.431s
添加了来自37个贡献者的50个软件包,并在22.431s中审核了143个软件包
而后,咱们须要运行node两个选项:--polyglot说咱们想访问其余语言,而且--jvm由于node默认状况下本机图像不包括JavaScript。
$ node --polyglot --jvm color-server.js serving at http://localhost:8080
而后像在浏览器中同样正常打开http:// localhost:8080 / css / aquamarine或其余颜色名称。
让咱们尝试使用更多语言和模块的更大示例。
JavaScript对于任意大的整数没有很好的解决方案。我发现了几个相似的模块,big-integer可是这些模块效率不高,由于它们将数字的组件存储为JavaScript浮点数。Java的BigInteger类效率更高,所以让咱们使用它来执行一些任意大的整数运算。
JavaScript还不包括对图形的任何内置支持,而R确实对此提供了出色的支持。让咱们使用R的svg模块绘制三角函数的3D散点图。
在这两种状况下,咱们均可以使用GraalVM的polyglot API,而且能够将其余语言的结果组合到JavaScript中。
const express = require('express') const app = express() const BigInteger = Java.type('java.math.BigInteger') app.get('/', function (req, res) { var text = 'Hello World from Graal.js!<br> ' // Using Java standard library classes text += BigInteger.valueOf(10).pow(100) .add(BigInteger.valueOf(43)).toString() + '<br>' // Using R interoperability to create graphs text += Polyglot.eval('R', `svg(); require(lattice); x <- 1:100 y <- sin(x/10) z <- cos(x^1.3/(runif(1)*5+10)) print(cloud(x~y*z, main="cloud plot")) grDevices:::svg.off() `); res.send(text) }) app.listen(3000, function () { console.log('Example app listening on port 3000!') })
在浏览器中打开http:// localhost:3000 /以查看结果。
这是咱们使用GraalVM能够作的第三件事-运行以多种语言编写的程序,并一块儿使用这些语言中的模块。咱们认为这是一种语言和模块的商品化,您可使用最适合您的问题的任何一种语言,以及想要的任何库,不管它来自哪一种语言。
GraalVM支持的另外一种语言是C。GraalVM能够以与运行JavaScript和Ruby等语言相同的方式运行C代码。
GraalVM实际支持的是运行LLVM工具链的输出(即LLVM位代码),而不是直接支持C。这意味着您能够将现有工具与C以及其余能够输出LLVM的语言一块儿使用,例如C ++,Fortran和可能的语言。其余语言。为了使演示更简单,我运行了一个特殊的gzip 单文件版本,该版本由Stephen McCamant维护。为了简单起见,它只是gzip源代码和autoconf配置串联到一个文件中。我还必须修补一些东西,以使其在macOS和clang上都能正常工做,但并不能使其在GraalVM上正常工做。
而后,咱们可使用标准clang(LLVM C编译器)进行编译,而且但愿将其编译为LLVM位代码,而不是本机程序集,由于这就是GraalVM能够运行的。我正在使用clang4.0.1。
$ clang -c -emit-llvm gzip.c
而后,咱们使用lli命令(LLVM位代码解释器)使用GraalVM直接运行此代码。让咱们尝试使用个人system压缩文件gzip,而后使用gzip在GraalVM上运行来解压缩。
$ cat small.txt Lorem ipsum dolor sit amet... $ gzip small.txt $ lli gzip.bc -d small.txt.gz $ cat small.txt Lorem ipsum dolor sit amet...
另外,也可使用clangGraalVM附带的C / C ++代码编译为LLVM位代码。为此,您应该启用预构建的LLVM工具链支持,并将LLVM_TOOLCHAIN环境变量指向包含一组构建工具(例如C编译器和连接器)的目录,该工具能够将本机项目编译为位代码。
$ gu install llvm-toolchain $ export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)
而后,您能够将gzip.c源代码编译为具备嵌入式LLVM位代码的可执行文件,并按如下方式运行它:
$ $LLVM_TOOLCHAIN/clang gzip.c -o gzip $ gzip small.txt $ lli gzip -d small.txt.gz $ cat small.txt Lorem ipsum dolor sit amet...
GraalVM中的Ruby和Python实现使用此技术来为这些语言运行C扩展。这意味着您能够在VM内运行C扩展,即便咱们支持这些旧的本机扩展接口,它也能够保持高性能。
这是使用GraalVM能够完成的第四件事-运行以诸如C和C ++之类的本地语言编写的程序,还能够运行针对诸如Python和Ruby之类的语言的C扩展,而现有的JVM实现(如JRuby)则没法作到。
若是您使用Java编程,则可能已经习惯了很是高质量的工具,例如IDE,调试器和分析器。并不是全部语言都具备这类工具,可是若是您在GraalVM中使用一种语言,则可使用它们。
全部GraalVM语言(目前不包括Java)都是使用通用的Truffle框架实现的。这使咱们能够一次实现调试器之类的功能,并使其对全部语言均可用。
要尝试此操做,咱们将编写一个基本的FizzBuzz程序,由于它会将内容打印到屏幕上,而且具备仅在某些迭代中使用的清晰分支,所以咱们能够更轻松地设置一些断点。咱们将从JavaScript实现开始。
function fizzbuzz(n) { if ((n % 3 == 0) && (n % 5 == 0)) { return 'FizzBuzz'; } else if (n % 3 == 0) { return 'Fizz'; } else if (n % 5 == 0) { return 'Buzz'; } else { return n; } } for (var n = 1; n <= 20; n++) { print(fizzbuzz(n)); }
咱们可使用js可执行文件,使用GraalVM正常运行此JavaScript程序。
$ js fizzbuzz.js 1 2 Fizz 4 Buzz Fizz ...
咱们还可使用flag运行程序--inspect。这将为咱们提供一个可在Chrome中打开的连接,并将在调试器中暂停该程序。
$ js --inspect fizzbuzz.js Debugger listening on port 9229. To start debugging, open the following URL in Chrome: chrome-devtools://devtools/bundled/inspector.html?ws=127.0.0.1:9229/6c478d4e-1350b196b409 ...
而后,咱们能够在FizzBuzz行上设置一个断点,而后继续执行。中断时,咱们将看到的值n,而且能够再次继续,或者浏览调试接口的其他部分。
Chrome调试器一般与JavaScript一块儿使用,可是GraalVM中的JavaScript没有什么特别的。此标志也可用,而且能够在咱们的Python,Ruby和R的实现中使用。我不会向您显示每一个程序的源,可是您以彻底相同的方式运行它们,而且为每一个程序得到相同的Chrome调试器接口。
$ graalpython --inspect fizzbuzz.py
$ ruby --inspect fizzbuzz.rb
$ Rscript --inspect fizzbuzz.r
您可能已经从Java使用过的另外一个工具是VisualVM。它为您提供了一个用户界面,您能够将其链接到计算机上或网络上某个位置上正在运行的JVM,以检查各个方面,例如它如何使用内存和线程。
GraalVM将VisualVM包含在标准jvisualvm命令中。
$ jvisualvm &> /dev/null &
若是在TopTen从前运行Java 应用程序的同时运行它,咱们能够观察一段时间内的内存使用状况,也能够执行堆转储并检查在堆中使用内存的对象类型。
$ java TopTen large.txt
我已经编写了这个Ruby程序来随着时间的推移产生一些垃圾。
require 'erb' x = 42 template = ERB.new <<-EOF The value of x is: <%= x %> EOF loop do puts template.result(binding) end
若是您使用VisualVM运行JRuby之类的标准JVM语言,您将很失望,由于您将看到底层的Java对象,而不是有关该语言对象的任何信息。
若是咱们改用GraalVM版本的Ruby,VisualVM将识别Ruby对象自己。咱们须要使用--jvm命令来使用VisualVM,由于它不支持Ruby的本机版本。
$ ruby --jvm render.rb
若是须要,咱们能够看到相同的基础Java对象的堆视图转储,或者在“ 摘要”下能够选择Ruby Heap并查看适当的Ruby对象。
Truffle框架是语言和工具的一种纽带。若是您使用Truffle来编程语言,而且使用Truffle的工具API来对诸如调试器之类的工具进行编程,则每种工具均可以使用每种语言,而且只须要编写一次该工具便可。
所以,可使用GraalVM的第五种方式是做为一个平台来获取语言的高质量工具,这些语言并不老是支持它们来构建定制工具,例如Chrome Debugger或VisualVM。
这些语言和工具不只能够用做独立的语言实现,并且能够在多种语言的用例中一块儿使用,也能够嵌入到Java应用程序中。使用新的org.graalvm.polyglotAPI,您能够加载和运行其余语言的代码,并使用它们中的值。
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; public class ExtendJava { public static void main(String[] args) { String language = "js"; try (Context context = Context.newBuilder().allowNativeAccess(true).build()) { for (String arg : args) { if (arg.startsWith("-")) { language = arg.substring(1); } else { Value v = context.eval(language, arg); System.out.println(v); } } } } }
若是您使用GraalVM中的javacand java命令,则导入org.graalvm...将已经在您的类路径中,所以您能够编译并运行此代码而无需任何额外的标志。
$ javac ExtendJava.java $ java ExtendJava '14 + 2' 16 $ java ExtendJava -js 'Math.sqrt(14)' 3.7416573867739413 $ java ExtendJava -python '[2**n for n in range(0, 8)]' [1, 2, 4, 8, 16, 32, 64, 128] $ java ExtendJava -ruby '[4, 2, 3].sort' [2, 3, 4]
这些版本的语言是相同的高性能多语种版本,你使用像命令获得node和ruby做为GraalVM可执行文件。
这是使用GraalVM的第六种方法—做为将Java语言中嵌入许多不一样语言的单个界面。使用polyglot API,您能够获取来宾语言对象并将其用做Java接口和其余复杂的互操做性。
GraalVM已经包括一个像这样构建的本机库—它是一个库,可以让您运行从本机应用程序以任何GraalVM语言编写的代码。像V8这样的JavaScript运行时,以及像CPython这样的Python解释程序,一般都是可嵌入的,这意味着它们能够做为一个库连接到另外一个应用程序中。经过连接到这一多语言嵌入库,GraalVM容许您在嵌入式上下文中使用任何语言。
获取GraalVM时已经构建了该库,可是默认状况下它仅包含内置语言JavaScript。您可使用下面的命令来重建多语种库,以包括其余语言,可是您须要从OTN下载基于Mac OS(19.3.0)的JDK8的Oracle GraalVM Enterprise Edition Native Image Early Adopter 。重建确实须要花费几分钟,所以,若是您遵循如下步骤,则可能只想尝试使用JavaScript –若是只须要JavaScript,就不须要重建。
$ gu install --force --file native-image-installable-svm-svmee-java8-darwin-amd64-19.3.0.jar $ gu rebuild-images libpolyglot
咱们能够编写一个简单的C程序,以任何经过命令行传递的GraalVM语言运行命令。咱们ExtendJava将从上面的示例开始作等效的工做,可是将C做为宿主语言。
#include <stdlib.h> #include <stdio.h> #include <polyglot_api.h> int main(int argc, char **argv) { poly_isolate isolate = NULL; poly_thread thread = NULL; if (poly_create_isolate(NULL, &isolate, &thread) != poly_ok) { fprintf(stderr, "poly_create_isolate error\n"); return 1; } poly_context context = NULL; if (poly_create_context(thread, NULL, 0, &context) != poly_ok) { fprintf(stderr, "poly_create_context error\n"); goto exit_isolate; } char* language = "js"; for (int n = 1; n < argc; n++) { if (argv[n][0] == '-') { language = &argv[n][1]; } else { poly_value result = NULL; if (poly_open_handle_scope(thread) != poly_ok) { fprintf(stderr, "poly_open_handle_scope error\n"); goto exit_context; } if (poly_context_eval(thread, context, language, "eval", argv[n], &result) != poly_ok) { fprintf(stderr, "poly_context_eval error\n"); const poly_extended_error_info *error; if (poly_get_last_error_info(thread, &error) != poly_ok) { fprintf(stderr, "poly_get_last_error_info error\n"); goto exit_scope; } fprintf(stderr, "%s\n", error->error_message); goto exit_scope; } char buffer[1024]; size_t length; if (poly_value_to_string_utf8(thread, result, buffer, sizeof(buffer), &length) != poly_ok) { fprintf(stderr, "poly_value_to_string_utf8 error\n"); goto exit_scope; } if (poly_close_handle_scope(thread) != poly_ok) { fprintf(stderr, "poly_close_handle_scope error\n"); goto exit_context; } buffer[length] = '\0'; printf("%s\n", buffer); } } if (poly_context_close(thread, context, true) != poly_ok) { fprintf(stderr, "poly_context_close error\n"); goto exit_isolate; } if (poly_tear_down_isolate(thread) != poly_ok) { fprintf(stderr, "poly_tear_down_isolate error\n"); return 1; } return 0; exit_scope: poly_close_handle_scope(thread); exit_context: poly_context_close(thread, context, true); exit_isolate: poly_tear_down_isolate(thread); return 1; }
而后,咱们可使用系统C编译器来编译和运行该程序,并连接到GraalVM中的本机多语言库。一样,它不须要JVM。
$ clang -L$GRAALVM_HOME/jre/lib/polyglot -I${GRAALVM_HOME}/jre/lib/polyglot -lpolyglot -o extendc -O1 extendc.c -rpath $GRAALVM_HOME $ otool -L extendc extendc: @rpath/jre/lib/polyglot/libpolyglot.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
$ ./extendc '14 + 2' 16 $ ./extendc -js 'Math.sqrt(14)' 3.7416573867739413 $ ./extendc -python '[2**n for n in range(0, 8)]' [1, 2, 4, 8, 16, 32, 64, 128] $ ./extendc -ruby '(0...8).map { |n| 2 ** n }' [1, 2, 4, 8, 16, 32, 64, 128]
您可使用GraalVM完成这第七件事-在本机应用程序中使用单个库来嵌入任何GraalVM语言。
Java具备许多高质量的库的强大生态系统,这些库一般在其余生态系统(包括本机应用程序和其余托管语言)中不可用。若是您想使用本机应用程序中的Java库,则能够嵌入JVM,但这会变得很是庞大和复杂。
GraalVM使您能够采用现成的Java库或本身编写的Java库,并将其编译为独立的本机库以供其余本机语言使用。与以前的本机编译同样,它们不须要运行JVM。
我编写了一个应用程序,该应用程序使用了出色的Apache SIS地理空间库来计算地球上两点之间的大圆距离。我使用SIS 0.8,我从http://sis.apache.org/单独下...。
import org.apache.sis.distance.DistanceUtils; public class Distance { public static void main(String[] args) { final double aLat = Double.parseDouble(args[0]); final double aLong = Double.parseDouble(args[1]); final double bLat = Double.parseDouble(args[2]); final double bLong = Double.parseDouble(args[3]); System.out.printf("%f km%n", DistanceUtils.getHaversineDistance(aLat, aLong, bLat, bLong)); } }
咱们能够像日常同样编译它,而后用它来计算伦敦(纬度51.507222,经度-0.1275)和纽约(40.7127,-74.0059)之间的距离。
$ javac -cp sis.jar -parameters Distance.java $ java -cp sis.jar:. Distance 51.507222 -0.1275 40.7127 -74.0059 5570.25 km
就像咱们对topten程序所作的那样,咱们能够将其编译为本地可执行文件。
$ native-image --no-server --no-fallback -cp sis.jar:. Distance ... $ ./distance 51.507222 -0.1275 40.7127 -74.0059 5570.25 km
咱们还能够将其构建为本地共享库,而不是可执行文件。为此,咱们将一个或多个方法声明为@CEntryPoint。
... import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.c.function.CEntryPoint; public class Distance { ... @CEntryPoint(name = "distance") public static double distance(IsolateThread thread, double a_lat, double a_long, double b_lat, double b_long) { return DistanceUtils.getHaversineDistance(a_lat, a_long, b_lat, b_long); } ... }
咱们不须要更改javac命令行,由于GraalVM会自动将这些新API放到类路径中。而后,咱们能够编译为共享库和自动生成的头文件。
$ native-image --no-server -cp sis.jar:. --shared -H:Name=libdistance $ otool -L libdistance.dylib # .so on Linux libdistance.dylib: .../graalvm-ten-things/libdistance.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1) CoreFoundation (compatibility version 150.0.0, current version 1575.17.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11) $ du -h libdistance.dylib 1.8M libdistance.dylib
而后,咱们能够编写一些C程序来使用该库。咱们的本机库的接口确实有一个小小的仪式-由于VM须要管理堆,线程,垃圾收集器和其余服务,所以咱们须要建立系统实例,并将其告知咱们的主线程。
#include <stdlib.h> #include <stdio.h> #include <libdistance.h> int main(int argc, char **argv) { graal_isolate_t *isolate = NULL; graal_isolatethread_t *thread = NULL; if (graal_create_isolate(NULL, &isolate, &thread) != 0) { fprintf(stderr, "graal_create_isolate error\n"); return 1; } double a_lat = strtod(argv[1], NULL); double a_long = strtod(argv[2], NULL); double b_lat = strtod(argv[3], NULL); double b_long = strtod(argv[4], NULL); printf("%.2f km\n", distance(thread, a_lat, a_long, b_lat, b_long)); if (graal_detach_thread(thread) != 0) { fprintf(stderr, "graal_detach_thread error\n"); return 1; } return 0; }
咱们使用标准系统工具对此进行编译,而且能够运行咱们的可执行文件(LD_LIBRARY_PATH=.在Linux上设置)。
$ clang -I. -L. -ldistance distance.c -o distance $ otool -L distance distance: .../graalvm-blog-post/libdistance.dylib (compatibility version 0.0.0, current version 0.0.0) libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1) $ ./distance 51.507222 -0.1275 40.7127 -74.0059 5570.25 km
这是咱们使用GraalVM能够完成的第八件事-将Java代码编译为一个本机库,而后能够在本机应用程序中使用它而无需使用完整的JVM。
用于嵌入语言的多语言库的一种应用是在Oracle数据库中。咱们已经使用它来建立Oracle数据库多语言引擎(MLE),其中包括对使用GraalVM语言和SQL模块的支持。
例如,假设咱们已经有一个用JavaScript编写的前端,而且咱们正在使用JavaScript模块对电子邮件地址进行一些验证validator。若是咱们对用SQL或PLSQL编写的数据库中的同一应用程序具备某种逻辑,咱们但愿可以使用彻底相同的验证器,以使结果相同。
您能够从https://oracle.github.io/orac...。而后将其加载到Docker中。
$ docker load --input mle-docker-0.2.7.tar.gz
咱们要运行该映像,而后在完成加载(可能须要几分钟)后,在其中执行一个Bash终端。
$ docker run mle-docker-0.2.7 $ docker ps $ docker exec -ti <container_id> bash -li
若是咱们能够sqlplus在此Bash终端中运行交互式SQL工具,以链接到数据库,则它已启动并正在运行。
$ sqlplus scott/tiger@localhost:1521/ORCLCDB
如今,仍然在Docker中运行的Bash终端中,咱们安装该validator模块,而后运行命令dbjs将其部署到数据库中。而后,咱们sqlplus再次运行。
$ npm install validator $ npm install @types/validator $ dbjs deploy -u scott -p tiger -c localhost:1521/ORCLCDB validator $ sqlplus scott/tiger@localhost:1521/ORCLCDB
如今,咱们能够将validator模块用做SQL表达式的一部分。
SQL> select validator.isEmail('hello.world@oracle.com') from dual; VALIDATOR.ISEMAIL('HELLO.WORLD@ORACLE.COM') ------------------------------------------- 1 SQL> select validator.isEmail('hello.world') from dual; VALIDATOR.ISEMAIL('HELLO.WORLD') -------------------------------- 0
这是咱们使用GraalVM能够作的第九件事-在Oracle数据库内部运行GraalVM语言,以便您能够在数据库逻辑内部的前端或后端使用相同的逻辑,而没必要始终将其从数据库中拉出到应用服务器。
Oracle实验室和咱们的学术合做者已经可以用一个相对较小的团队来实现JavaScript,R,Ruby,Python和C的新高性能实现,由于咱们已经开发了Truffle框架来简化这一过程。
Truffle是一个Java库,可帮助您编写语言的抽象语法树(AST)解释程序。AST解释器多是实现语言的最简单方法,由于它直接在解析器的输出上工做,而且不涉及任何字节码或常规编译器技术,可是一般很慢。所以,咱们将其与称为部分评估的技术相结合,该技术容许Truffle仅仅基于AST解释器就可使用GraalVM编译器为您的语言自动提供即时编译。
您可使用Truffle来实现本身的新编程语言,建立现有编程语言的高性能实现或实现特定于域的语言。咱们在项目中谈论了不少关于Truffle和Graal的细节,可是咱们经常忘记说起Truffle是实现语言的简单方法。您会自动得到调试器之类的功能。任何只完成了编程语言实施本科课程的人都应该具有所需的基本技能。Oracle实验室只需几个月的实习生,就能比之前的任何工做更快地实现Ruby的基本版本。
咱们这里没有空间来显示完整的语言,即便是很小的一种语言,可是SimpleLanguage是一个可执行的教程,说明如何使用Truffle基于简化的JavaScript样式语言来建立本身的语言。例如看实现了的if说法。
Oracle实验室之外的其余人使用Truffle编写的其余语言包括Smalltalk变体,Newspeak变体和Lisp变体。Lisp示例包含一个您能够遵循的教程。
GraalVM提供了很是多样化的新功能集–在该平台上,您能够构建更强大的语言和工具,并将其置于更多环境中。它使您能够选择所需的语言和模块,不管程序在何处运行或已在使用哪一种语言。
要尝试GraalVM,请访问https://www.graalvm.org/。那里有下载和文档的连接,还有更多相似咱们在此博客文章中显示的示例。
本篇文章由一文多发平台ArtiPub自动发布