オラクルのJava実装はオープンソースのOpenJDKプロジェクトがベースである。そしてOpenJDKには長くJava 1.3からHotSpot仮想マシンがある。HotSpotはJITコンパイラを2つ持っており、それらはC1とC2として知られている ("クライアント"と"サーバ"と呼ばれることもある)。現代的なJava環境では標準的なプログラムの実行中にJITコンパイラを2つとも使う。
Javaプログラムはインタプリタモードで動き始める。実行が少し進むと、頻繁に呼び出されるメソッドが特定されコンパイルされる。初めはC1を使い、HotSpotがさらに多くの呼び出しを検知するとメソッドはC2を使って再度コンパイルされる。この戦略は"階層型コンパイル"として知られ、HotSpotのデフォルトの処理である。
ほとんどのJavaアプリケーションにとって、C2コンパイラが環境のもっとも重要なパーツの1つであるということになる。これがプログラムのもっとも重要な部分に対し、高度に最適化されたマシンコードを生成するからである。
C2は非常に出来がよく、C++に匹敵する (もしくはそれより速い) コードを生成できる。gccやGoコンパイラのような事前 (AOT) コンパイラでは利用できない実行時最適化のおかげだ。
しかし、C2は近年成果が落ちてきており、ここ数年コンパイラに大幅な改善は実装されていない。それだけでなく、C2のコードは維持拡張がとても難しくなっており、新しく関わるエンジニアがC++の方言で書かれたコードベースが持つスピードに追いつくのは非常に困難である。
実際、現在の設計では大幅な拡張は不可能だと広く考えられている (Twitterのような企業やCliff Clickのようなエキスパートからだ)。C2に残っている改善点はすべて、やや取るに足らないものだけとなるだろう。
最近のリリースで改善が見られた領域はJVMの組み込み関数をより利用するという部分だけであり、そのテクニックはドキュメントに記述されている (@HotSpotIntrinsicCandidateアノテーション)。これは次のようなものだ。
HotSpot VMがアノテーションを付与されたメソッドを手書きのアセンブリや手書きのコンパイラIR、すなわちパフォーマンス改善用のコンパイラ組み込み関数で置き換える場合、メソッドは強化されます。
JVMを起動すると、実行しているプロセッサが精査される。これによりJVMはそのCPUで利用可能な機能を正確に把握できるようになる。使用中のプロセッサ特有の組み込み関数テーブルを構築する。JVMはハードウェアの能力を十分に活用できるということだ。
これはAOTコンパイルと異なる。AOTコンパイルは一般的なチップ向けにコンパイルし、利用できる機能について仮説を慎重に立てなければならない。AOTコンパイルされたライブラリは実行時に現在のCPUでサポートされていない命令を実行しようとするとクラッシュするからだ。
HotSpotはすでにかなりの数の組み込み関数をサポートしている。たとえばコンペア・アンド・スワップ (CAS) 命令がよく知られている。これを使ってatomic integerといった機能を実装している。現代的なプロセッサはほぼすべて、単一のハードウェア命令を使ってこれを実装している。
組み込み関数はJVMに事前に知らされるもので、OSやCPUアーキテクチャでの特定の機能のサポート内容に依存する。これにより組み込み関数はプラットフォーム特有となり、全プラットフォームですべての組み込み関数がサポートされるわけではない。
一般的に組み込み関数は全般的な技術ではなく、ポイント修正として認識されるべきだ。パワフルで軽量、柔軟という利点はあるが、複数のアーキテクチャにまたがってサポートしなければならないため開発と維持のコストが潜在的に高い。
ゆえに、組み込み関数で進展があったにもかかわらず、あらゆる意味でC2はそのライフサイクルの終焉にたどり着き、置き換えられなければならないものとなった。
オラクルは最近GraalVMの最初のリリースを発表している。研究プロジェクトであり、いつかはHotSpotを完全に置き換えることになるかもしれない。
Java開発者にとって、Graalはいくつか別々の、しかし関連のある複数のプロジェクトとみなせる。HotSpotの新しいJITコンパイラであり、また新しいpolyglotな仮想マシンである。以降JITコンパイラはGraal、新しいVMはGraalVMとして言及する。
Graalの試みの全体的な目標は、Javaに対するコンパイル方法の見直しである (そしてGraalVMの場合他言語にも同様のことをする)。Graalの基本的な見解はとても単純だ。
Javaの (JIT) コンパイラは、バイトコードをマシンコードに変換するものだ。Javaの用語では単に |
Javaでコンパイラを書くと大きな利点がいくつかあるとわかる。次のようなことだ。
- コンパイラエンジニアが新規に加わる際のハードルが非常に低くなる。
- コンパイラにおいてメモリ安全になる。
- コンパイラ開発で成熟したJavaのツール群を活用できる。
- 新しいコンパイラ機能のプロトタイプをより早く作れる。
- コンパイラがHotSpotから独立したものとなる。
- コンパイラが自分自身をコンパイルでき、自分自身のより速いJITコンパイルされたバージョンを生成できる。
Graalは新しいJVMコンパイラインタフェース (JVMCI) を使っている。これはHotSpotにプラグインできるようJEP 243として提供されているが、GraalVMの主要な部分としても使われている。この技術はもう存在しており、現時点でリリースされているが、Java 10ではまだかなり実験的な技術である。次のようにすると新しいJITコンパイラを使える。
-XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler
単純なプログラムを実行するのにも異なる3つの方法があるということになる。通常の階層型となるコンパイラで、もしくはGraalのJVMCIバージョンで、そしてGraalVM自身で、である。
単純な例を使ってGraalの効果をを見てみよう。これでもコンパイラの起動を見るには十分な実行時間だ。単純な文字列のハッシュ化をする。
package kathik;
public final class StringHash {
public static void main(String[] args) {
StringHash sh = new StringHash();
sh.run();
}
void run() {
for (int i=1; i<2_000; i++) {
timeHashing(i, 'x');
}
}
void timeHashing(int length, char c) {
final StringBuilder sb = new StringBuilder();
for (int j = 0; j < length * 1_000_000; j++) {
sb.append(c);
}
final String s = sb.toString();
final long now = System.nanoTime();
final int hash = s.hashCode();
final long duration = System.nanoTime() - now;
System.out.println("Length: "+ length +" took: "+ duration +" ns");
}
}
このコードをPrintCompilation
フラグを通常の方法でセットして実行する。どのメソッドがコンパイルされたかを見るためだ (またGraalの実行と比較する際のベースラインとなる)。
java -XX:+PrintCompilation -cp target/classes/ kathik.StringHash > out.txt
Java 10で実行しコンパイラとしてのGraalの効果を見てみる。
java -XX:+PrintCompilation \
-XX:+UnlockExperimentalVMOptions \
-XX:+EnableJVMCI \
-XX:+UseJVMCICompiler \
-cp target/classes/ \
kathik.StringHash > out-jvmci.txt
そしてGraalVMでも実行する。
java -XX:+PrintCompilation \
-cp target/classes/ \
kathik.StringHash > out-graal.txt
出力としてファイルが3つ生成される。生成される出力をtimeHashing()
の実行200回分で切り詰めた場合、次のようになるだろう。
$ ls -larth out*
-rw-r--r-- 1 ben staff 18K 4 Jun 13:02 out.txt
-rw-r--r-- 1 ben staff 591K 4 Jun 13:03 out-graal.txt
-rw-r--r-- 1 ben staff 367K 4 Jun 13:03 out-jvmci.txt
予想どおり、Graalを使った実行では出力をより多く生成している。これはPrintCompilationの出力の違いによるものだ。これはまったく驚くべきことではない。Graalの大事なポイントはJITコンパイラが最初にコンパイルされるものの1つとなるということだ。VM起動後の最初の数秒間はJITコンパイラのウォームアップが多くあるだろう。
Graalコンパイラを使ったJava 10の実行の出力から、最初の一部分を見てみよう (通常のPrintCompilationフォーマットだ) 。
$ grep graal out-jvmci.txt | head
229 293 3 org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory::adjustCompilationLevelInternal (70 bytes)
229 294 3 org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory::checkGraalCompileOnlyFilter (95 bytes)
231 298 3 org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory::adjustCompilationLevel (9 bytes)
353 414 ! 1 org.graalvm.compiler.serviceprovider.JDK9Method::invoke (51 bytes)
354 415 1 org.graalvm.compiler.serviceprovider.JDK9Method::checkAvailability (37 bytes)
388 440 1 org.graalvm.compiler.hotspot.HotSpotForeignCallLinkageImpl::asJavaType (32 bytes)
389 441 1 org.graalvm.compiler.hotspot.word.HotSpotWordTypes::isWord (31 bytes)
389 443 1 org.graalvm.compiler.core.common.spi.ForeignCallDescriptor::getResultType (5 bytes)
390 445 1 org.graalvm.util.impl.EconomicMapImpl::getHashTableSize (43 bytes)
390 447 1 org.graalvm.util.impl.EconomicMapImpl::getRawValue (11 bytes)
こうした小さな実験は少し注意深く扱うべきである。たとえば、初期の大量のコンパイルがスクリーンI/Oに影響しウォームアップのパフォーマンスがゆがめられるかもしれない。それだけでなく、増え続ける文字列をアロケートするバッファが徐々にHumongousリージョン (巨大なオブジェクト向けにG1コレクタによって予約される特別なリージョン) にアロケートしなければならないほど大きくなるだろう。Java 10もGraalVMもともにデフォルトでG1コレクタを使う。しばらくするとG1ガベージコレクションのプロファイルはG1のHumongousコレクションによって特色づけられる。これはまったく普通の状況ではない。
GraalVMについて議論する前に、注目に値することがある。Java 10でGraalコンパイラを使う、もう1つ別の方法だ。AOTコンパイラモードである。
思い出してほしい。 (コンパイラとしての) Graalは新しくきれいなインタフェース (JVMCI) に従った、まったく新しいコンパイラとしてゼロから書かれている。この設計によりGraalはHotSpotと統合できるが、HotSpotに固定されてはいない。
ホットなメソッドだけをコンパイルするプロファイル駆動のアプローチを使うのではなく、我々はコードを実行することなくオフラインモードですべてのメソッドを完全にコンパイルするためにGraalを使うことも考えられる。これがJEP 295の"AOTコンパイル"で言及されている機能だ。
HotSpot環境では、これを使って共有オブジェクト/ライブラリを生成できる (Linuxの.so
やMacの.dylib
)。次のようにする。
$ jaotc --output libStringHash.dylib kathik/StringHash.class
以降の実行ではコンパイルしたコードが使える。
$ java -XX:AOTLibrary=./libStringHash.dylib kathik.StringHash
ここでGraalを使うのは、たった1つの目的のためだ。スタートアップ時間のスピードを速くするためである。HotSpotにおける通常の階層型コンパイルのアプローチが引き継ぐまでの時間だ。絶対的には、フルサイズのアプリケーションにおいてJITコンパイルは実際のベンチマークでAOTコンパイルされたコードより効率がよいと考えられる。しかし、詳細はワークロードに依存する。
AOTコンパイル技術はまだ試作段階で、技術的にはlinux/x64しか (しかも実験的に) サポートしていない。たとえば、Mac上でjava.baseモジュールをコンパイルしようとすると、以下のエラーが発生する (.dylib
はそれでも生成されるが)。
$ jaotc --output libjava.base.dylib --module java.base
Error: Failed compilation: sun.reflect.misc.Trampoline.invoke(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: java.lang.Error: Trampoline must not be defined by the bootstrap classloader
at parsing java.base@10/sun.reflect.misc.Trampoline.invoke(MethodUtil.java:70)
Error: Failed compilation: sun.reflect.misc.Trampoline.<clinit>()V: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: java.lang.NoClassDefFoundError: Could not initialize class sun.reflect.misc.Trampoline
at parsing java.base@10/sun.reflect.misc.Trampoline.<clinit>(MethodUtil.java:50)
こうしたエラーは特定のメソッドをAOTコンパイルから除外するよう制御できる。コンパイラディレクティブのファイルを使う (詳細はJEP 295のページを参照してほしい)。
コンパイラのエラーにもかかわらず、このAOTコンパイルしたbaseモジュールはユーザコードと一緒に実行できる。次のようにする。
java -XX:+PrintCompilation \
-XX:AOTLibrary=./libStringHash.dylib,libjava.base.dylib \
kathik.StringHash
PrintCompilationをセットしてJITコンパイルがどのくらい動作したのかを見る。今度はほぼないに等しい。初期のブートストラップで必要となる本当にコアなメソッドのいくつかだけが今回JITコンパイルされる。
111 1 n 0 java.lang.Object::hashCode (native)
115 2 n 0 java.lang.Module::addExportsToAllUnnamed0 (native) (static)
結果として、この単純なJavaアプリケーションはほぼ100%AOTコンパイルの形式で実行していると判断できる。
GraalVMに戻って、このプラットフォームが提供する機能で核となるものを1つ見てみよう。GraalVMで実行するJavaアプリケーションに他言語を完全に組み込めるという機能だ。
これはJSR 223 (Javaプラットフォームのためのスクリプト) と対応するもの、もしくは置換するものとして考えられる。しかしGraalのアプローチはHotSpotにあった以前の機能の技術と比較するとより先を行っている。
この機能はGraalVMとGraal SDKに依存する。SDKはGraalVMのデフォルトのクラスパスの一部として提供されているが、IDEのプロジェクトで明示的に含めるべきだ。たとえばこうだ。
<dependency>
<groupId>org.graalvm</groupId>
<artifactId>graal-sdk</artifactId>
<version>1.0.0-rc1</version>
</dependency>
もっとも単純な例はHello Worldだ。GraalVMがデフォルトで同梱しているJavaScript実装を使ってみよう。
import org.graalvm.polyglot.Context;
public class HelloPolyglot {
public static void main(String[] args) {
System.out.println("Hello World: Java!");
Context context = Context.create();
context.eval("js", "print('Hello World: JavaScript!');");
}
}
これはGraalVMでは思ったとおりに動作するが、Java 10で実行するとGraal SDKを設定していても次の (驚くほどではない) エラーとなる。
$ java -cp target/classes:$HOME/.m2/repository/org/graalvm/graal-sdk/1.0.0-rc1/graal-sdk-1.0.0-rc1.jar kathik.HelloPolyglot
Hello Java!
Exception in thread "main" java.lang.IllegalStateException: No language and polyglot implementation was found on the classpath. Make sure the truffle-api.jar is on the classpath.
at org.graalvm.polyglot.Engine$PolyglotInvalid.noPolyglotImplementationFound(Engine.java:548)
at org.graalvm.polyglot.Engine$PolyglotInvalid.buildEngine(Engine.java:538)
at org.graalvm.polyglot.Engine$Builder.build(Engine.java:367)
at org.graalvm.polyglot.Context$Builder.build(Context.java:528)
at org.graalvm.polyglot.Context.create(Context.java:294)
at kathik.HelloPolyglot.main(HelloPolyglot.java:8)
これはTruffleがGraalVMでの実行のみに制限されているということである (少なくとも現時点では)。
他言語を扱う機能の一種はJava 6でのスクリプティングAPIの導入時から存在していた。Java 8でのNashornの到来で大きく強化された。これはinvokedynamic
ベースのJavaScript実装だった。
GraalVMの技術が違うところは、今のエコシステムに明示的に複数の言語実装用のSDKやサポートツールがあるということだ。 そして下層のVM上でそれらの言語を同等で相互運用可能なものとして実行できる。
この手法を進めるキーはTruffleと呼ばれるコンポーネントと、シンプルで必要最小限なJVMバイトコードを実行する機能を持つVM、SubstrateVMである。
Truffleは新しい言語実装用にSDKとツールを提供する。一般的な方法は以下のようなものである。
- 言語の文法から始める。
- パーサジェネレータを適用する (たとえばCoco/R)。
- Mavenを使ってインタプリタと単純な言語ランタイムを構築する。
- 構築された言語実装をGraalVM上で実行する。
- (JITモードの) Graalがこの新言語のパフォーマンスを自動的に向上させ始めるまで待機する。
- [オプション] AOTモードでGraalを使いインタプリタをネイティブランチャにコンパイルする。
追加の設定なくGraalVMはJVMバイトコードとJavaScript、LLVMをサポートする。他言語を呼び出してみよう。Rubyならこうなる。
context.eval("ruby", "puts \"Hello World: Ruby\"");
するとGraalVMは実行時例外をスローする。
Exception in thread "main" java.lang.IllegalStateException: A language with id 'ruby' is not installed. Installed languages are: [js, llvm].
at com.oracle.truffle.api.vm.PolyglotEngineImpl.requirePublicLanguage(PolyglotEngineImpl.java:559)
at com.oracle.truffle.api.vm.PolyglotContextImpl.requirePublicLanguage(PolyglotContextImpl.java:738)
at com.oracle.truffle.api.vm.PolyglotContextImpl.eval(PolyglotContextImpl.java:715)
at org.graalvm.polyglot.Context.eval(Context.java:311)
at org.graalvm.polyglot.Context.eval(Context.java:336)
at kathik.HelloPolyglot.main(HelloPolyglot.java:10)
(まだベータだが) TruffleバージョンのRubyを使うには、ダウンロードしてインストールする必要がある。GraalのバージョンRC1では (すぐにRC2となるだろうが)、次のようにする。
gu -v install -c org.graalvm.ruby
GraalVMを複数ユーザに対する標準の$JAVA_HOMEとしてシステム全体にインストールする場合はsudoが必要となることに注意してほしい。OSSでないEEエディションのGraalVM (現時点ではMac用のみ) を使う場合、もう1ステップ必要だ。Truffleインタプリタをネイティブコードに変換しよう。
ネイティブイメージ (ランチャ) を言語用に再ビルドするとパフォーマンスが改善されるが、これにはコマンドラインツールを使う必要がある。次のようにする (GraalVMをシステム全体にインストールしたと仮定しているので、ルート権限が必要だ)。
$ cd $JAVA_HOME
$ sudo jre/lib/svm/bin/rebuild-images ruby
これはまだ開発中のためいくつか手作業があるが、開発チームは徐々にこの手順をスムーズにしたいと考えている。
ネイティブコンポーネントの再ビルドで何か問題に遭遇しても、心配ない。ネイティブイメージの再ビルドがなくても依然として動作はする。
polyglotコーディングのもっと複雑な例を見てみよう。
Context context = Context.newBuilder().allowAllAccess(true).build();
Value sayHello = context.eval("ruby",
"class HelloWorld\n" +
" def hello(name)\n" +
" \"Hello #{name}\"\n" +
" end\n" +
"end\n" +
"hi = HelloWorld.new\n" +
"hi.hello(\"Ruby\")\n");
String rubySays = sayHello.as(String.class);
Value jsFunc = context.eval("js",
"function(x) print('Hello World: JavaScript with '+ x +'!');");
jsFunc.execute(rubySays);
このコードはやや読みづらいが、TruffleRubyとJavaScriptの両方を使っている。初めにRubyコードを少し呼び出している。
class HelloWorld
def hello(name)
"Hello #{name}"
end
end
hi = HelloWorld.new
hi.hello("Ruby")
これは新しくRubyのクラスを定義し、そこにメソッドを定義し、Rubyオブジェクトをインスタンス化して、そのhello()
メソッドを呼び出す。このメソッドは (Rubyの) 文字列を返す。この文字列を強制的にJavaランタイムにおけるJavaの文字列にする。
そしてJavaScriptの単純な無名関数を作る。次のようなものだ。
function(x) print('Hello World: JavaScript with '+ x +'!');
この関数をexecute()を通じて呼び出し、Rubyの呼び出し結果を関数に渡す。関数はJSランタイム内から出力をする。
Contextオブジェクトを生成する際に、Contextへのアクセスを拡大する許可が必要なことに注意してほしい。これはRuby向けで、JSでは必要なかったので、セットアップがより複雑な構造になっている。これは現在のRuby実装の限界で、将来的には取り除かれるかもしれない。
polyglotの最後の例を見てみよう。こんなことができる所まで来たのだ。
Value sayHello = context.eval("ruby",
"class HelloWorld\n" +
" def hello(name)\n" +
" \"Hello Ruby: #{name}\"\n" +
" end\n" +
"end\n" +
"hi = HelloWorld.new\n" +
"hi");
Value jsFunc = context.eval("js",
"function(x) print('Hello World: JS with '+ x.hello('Cross-call') +'!');");
jsFunc.execute(sayHello);
このバージョンでは実際のRubyオブジェクトを返しており、Stringではない。それだけでなく、いかなるJavaの型にも強制的に変換していない。代わりにJS関数にそのまま渡している。
function(x) print('Hello World: JS with '+ x.hello('Cross-call') +'!');
これは動作するし、期待したとおり出力する。
Hello World: Java!
Hello World: JS with Hello Ruby: Cross-call!
これはJSランタイムが別のランタイムにあるオブジェクトの外部メソッドを、(少なくとも単純なケースでは) シームレスな型変換で呼び出せるということを意味する。
まったく異なるセマンティクスと型システムを持つ言語間での相互変換というこの能力は長い間 (少なくとも10年間) JVMエンジニアの間で議論されていた。GraalVMの出現でメインストリームに向けて重要な一歩を踏み出したのだ。
これらの外部オブジェクトがGraalVMでどのように表現されるのか簡単に見てみよう。このちょっとしたJSを使い、渡されたRubyオブジェクトを単に出力するようにする。
function(x) print('Hello World: JS with '+ x +'!');
この出力は以下のようになる (もしくは似たようなものになる)。
Hello World: JS with foreign {is_a?: DynamicObject@540a903b<Method>, extend: DynamicObject@238acd0b<Method>, protected_methods: DynamicObject@34e20e6b<Method>, public_methods: DynamicObject@15ac59c2<Method>, ...}!
外部オブジェクトはDynamicObject
オブジェクトの袋詰めとして表現されていることを示している。こうしてセマンティックな操作を移譲し、多くの場合そのオブジェクトのホームのランタイムに戻す。
この記事を締めくくるには、ベンチマークとライセンスについて一言述べておくべきだ。GraalとGraalVMは大きな望みだが、現時点ではまだ初期ステージ/実験的技術であるとはっきりと理解しておかなければならない。
まだ汎用的なユースケース向けに最適化や本番対応がされておらずHotSpot/C2と同等になるまでには時間がかかるだろう。マイクロベンチマークは誤解を招くこともよくある。ある状況下での方法を示すことはできるが、結局本番アプリケーション全体に対するユーザレベルのベンチマークだけがパフォーマンス解析には重要である。
これについて考える方法の1つは、C2が本質的にパフォーマンスの局所的最大化であり設計ライフタイムの終わりにあるということだ。Graalは私たちに局所的最大化から抜け出し新たなよりよい領域へ移る、そしてその途中で私たちがVM設計やコンパイラについて知っていると思っている多くのことを潜在的に書き直す機会を与えてくれる。けれどもまだ成熟していない技術である。今後数年で完全にメインストリームとなる可能性は極めて低い。
ゆえに今日行われているパフォーマンステストはすべて細心の注意を持って解析すべきである、ということだ。パフォーマンステストの比較 (とくにHotSpot+C2対GraalVM) は、りんごとオレンジを比べるようなことだ。成熟し、本番品質のランタイム対超初期ステージの実験的ランタイムとなる。
またGraalVMのライセンス形態が今まで見たどんなものとも異なるだろう、ということを指摘する必要もある。オラクルがサンを買収したとき、彼らはすでに存在する、とても成熟したプロダクトとしてHotSpotを手に入れた。これはフリーソフトウェアとしてライセンスされていた。HotSpotのコアプロダクト上に付加価値を与え収益化しようとする、わずかな試みもあった。たとえば、UnlockCommercialFeatures
スイッチだ。こうした機能の終焉 (たとえばMission Controlのオープンソース化) でこのモデルは大きな商業的成功ではなかったと言える。
Graalは違う。オラクルの研究プロジェクトとして生涯を始めて、今は本番対応の製品へと進んでいる。オラクルはGraalを現実にするために多額を投資してきた。このプロジェクトで必要とされる個人やチームは不足しており、まったく安価ではない。異なる根本的技術をベースにしているので、オラクルはHotSpotと異なる商業モデルを利用するのは自由で、より幅広い顧客に渡ってGraalVMを収益化しようと試みれる。そこには現在HotSpotランタイムに対して支払いをしていない人も含まれる。オラクルがGraalVMの機能のいくつかをOracle Cloud上で実行している顧客だけが利用できるようにすることさえ可能だ。
今のところ、オラクルは開発および本番での使用が無償であるGPLライセンスのコミュニティ版 (CE) と、開発と評価での使用のみ無償であるエンタープライズ版 (EE) を公開している。両方のバージョンともオラクルのGraalVMのサイトからダウンロードでき、より詳細な情報もそこにある。
著者について
Ben Evans氏はJVMパフォーマンスの最適化をする企業であるjClarity社の共同設立者である。彼はLJC (ロンドンのJUG) のオーガナイザで、Javaエコシステムの標準を定義する手助けをするJCP Executive Committeeのメンバである。Ben氏はJavaチャンピオンであり、JavaOne Rockstarスピーカを3度受賞、"The Well-Grounded Java Developer"と新バージョンの"Java in a Nutshell"、"Optimizing Java"の著者である。彼はJavaプラットフォームやパフォーマンス、アーキテクチャ、並列性、スタートアップなどのトピックに関するスピーカーとして常連である。Ben氏は講演や研修、執筆、コンサルタントといった仕事をする時間もあるので、詳細については連絡してほしい。