GraalVM 要作的十件事

GraalVM有不少不一樣的部分,所以,若是您之前聽過這個名字,或者甚至看過咱們的一些談話,那麼確定能夠作的事情您還不知道。在本文中,咱們將列出GraalVM的一些不一樣功能,並向您展現它們能夠爲您作些什麼。css

  • 1.高性能現代Java
  • 2.佔地面積小,啓動迅速的Java
  • 3.結合使用JavaScript,Java,Ruby和R
  • 4.在JVM上運行本地語言
  • 5.適用於全部語言的工具
  • 6.擴展基於JVM的應用程序
  • 7.擴展本機應用程序
  • 8.Java代碼做爲本機庫
  • 9.數據庫中的多語種
  • 10.建立本身的語言

您能夠複製的一切,我展現這篇文章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)

1.高性能現代Java

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

2.佔地面積小,啓動迅速的Java

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映像。

3.結合使用JavaScript,Java,Ruby和R

除了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能夠作的第三件事-運行以多種語言編寫的程序,並一塊兒使用這些語言中的模塊。咱們認爲這是一種語言和模塊的商品化,您可使用最適合您的問題的任何一種語言,以及想要的任何庫,不管它來自哪一種語言。

4.在JVM上運行本地語言

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)則沒法作到。

5.適用於全部語言的工具

若是您使用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。

6.擴展基於JVM的應用程序

這些語言和工具不只能夠用做獨立的語言實現,並且能夠在多種語言的用例中一塊兒使用,也能夠嵌入到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接口和其餘複雜的互操做性。

7.擴展本機應用程序

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語言。

8. Java代碼做爲本機庫

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。

9.數據庫中的多語種

用於嵌入語言的多語言庫的一種應用是在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語言,以便您能夠在數據庫邏輯內部的前端或後端使用相同的邏輯,而沒必要始終將其從數據庫中拉出到應用服務器。

10.建立本身的語言

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自動發佈

相關文章
相關標籤/搜索