JavaやPython、JavaSciptなど、複数言語で記述されたアプリケーション実行のための共有ランタイムを提供する多言語仮想マシンのGraalVMが、Windowsプラットフォームをフルサポートしたメジャーバージョン20.0をリリースした。GraalVM 20.0 Windowsディストリビューションには、JavaScriptエンジン、GraalVMアップデートユーティリティのgu
、GraalVMコンパイラの使用可能なJDKが含まれている。LinuxやmacOS用のディストリビューションとは異なり、WindowsではまだNode.jsサポートが使用できない点には注意が必要だ。
GraalVMのネイティブイメージ生成ユーティリティもアップデートされて、Windows上で拡張サポートが提供されるようになった。ネイティブイメージは、事前(ahead-of-time)コンパイルされたJavaバイトコードをスタンドアロンの実行ファイルとしてパッケージしたもので、一般的に起動の高速化やフットプリントの縮小などのメリットがある。ネイティブイメージユーティリティは、GraalVMのアップデートユーティリティであるgu
を使ってインストールする。gu
は、GraalVMのコアディストリビューションに含まれないパッケージのダウンロードとインストールを目的としたパッケージマネージャである。
関連するニュースの中で、Apache Tomcat 9が、コンテナを含むGraalVMのネイティブイメージ生成をフルサポートするという発表があった。ただし、ネイティブイメージベースでTomcatを構築する場合には、TomcatのWebアプリが使用するサードパーティ製のライブラリがネイティブイメージをフルサポートしていない可能性がある点に注意しなければならない。例えば、あるライブラリが動的クラスローディングやリフレクションに依存している場合、ネイティブイメージの使用するコンフィギュレーションファイルを自動生成するために、最初にGraalVMベースのVMをトレーシングエージェントを使って実行しなければならない。生成されたファイルはWebアプリまたはjarのMETA-INF/native-image
配下において、ネイティブイメージ内のリフレクションなどの機能をサポートするために使用する。
以下のコード例は、組み込みTomcatサーバを実行するシンプルなJava 11クラスである。
public class TED {
public static void main(String... args)
throws Exception {
File baseFolder = new File(System.getProperty("user.dir"));
File appsFolder = new File(baseFolder, "web-apps");
var tomcat = new Tomcat();
tomcat.setBaseDir(baseFolder.getAbsolutePath());
tomcat.setPort(8080);
tomcat.getHost().setAppBase(appsFolder.getAbsolutePath());
// Call the connector to create the default connector.
tomcat.getConnector();
tomcat.getHost().setAppBase(appsFolder.getAbsolutePath());
var wrapper = tomcat.addServlet("", "hello", new HelloServlet());
wrapper.setLoadOnStartup(1);
wrapper.addMapping("/*");
tomcat.start();
tomcat.getServer().await();
}
private static class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setStatus(200);
var writer = resp.getWriter();
writer.write("Hello from Tomcat native image!");
writer.flush();
writer.close();
}
}
}
native-image
ユーティリティでは単一JARファイルの方が扱いやすいので、Maven ShadeプラグインやGradle Shadowプラグインを使って、クラスをuber JAR(実行可能なJARファイル)にコンパイルおよびビルドしておくとよいだろう。uber JARが完成すれば、GraalVMトレーシングエージェントを実行して、ネイティブイメージ用のコンフィギュレーションファイルを生成すればよい。以下のスニペットは、GraalVMトレーシングツールを使ってリフレクション、JNI(Java Native Service)、クラスパスリソース、動的プロキシ用のコンフィギュレーションファイルを生成する方法を示したものだ。
java \
-agentlib:native-image-agent=config-merge-dir=graal-conf \
-cp build/libs/ted-1.0-all.jar my.example.TED
トレーシングアージェントは、native-image
ユーティリティの入力となる、さまざまなコンフィギュレーションファイルをJSON形式で生成してくれる。
native-image --no-server \
-cp build/libs/ted-0.0.1-SNAPSHOT-all.jar \
--allow-incomplete-classpath \
-H:+JNI -H:+ReportUnsupportedElementsAtRuntime \
-H:+ReportExceptionStackTraces -H:EnableURLProtocols=http,jar,jrt \
-H:ConfigurationFileDirectories=graal-conf/ \
-H:ReflectionConfigurationFiles=graal-conf/reflect-config.json \
-H:ResourceConfigurationFiles=graal-conf/resource-config.json \
-H:JNIConfigurationFiles=graal-conf/jni-config.json \
my.example.TED ted
上の例では、ユーティリティが40MB程度のファイルとしてted
というネイティブバイナリを生成する。このファイルはJDKやJREなしで実行が可能である。
./ted
http://localhost:8006
にナビゲートされ、例に挙げたJavaクラスで定義したようなハローメッセージが表示されるはずだ。
Javaシリアライゼーション、JMX、JULI、tomcat-nativeの静的リンクなど、Tomcatのいくつかの機能は、GraalVMネイティブイメージ内ではまだサポートされていないため、注意が必要だ。
ネイティブイメージの改善とWindowsサポートに加えて、GraalVMのWebAssembly言語の実装であるGraalWasmが、試験的機能として利用可能になった。多言語APIを使用すれば、webassemblyバイナリをJavaアプリケーションに組み込んで実行することができる。
GraalVMのJavaに関して、もうひとつ注目に値する変更は、ThreadPriorityPolicy
のデフォルト設定が1になったことだ。これにより、Java内で設定されたスレッドのプライオリティが、OSのネイティブスレッドのプライオリティに反映されるようになる。ThreadPriorityPolicy
のデフォルト値はアプリケーションのパフォーマンスに影響する可能性があるため、パフォーマンスの重要なアプリケーションの移行時には注意する必要がある。
今回のリリースでは、ネイティブイメージ生成に関するJNIとJDK 11の問題のいくつかが解決されている。新しいJDK Flight Recordert Data Viewerが、VisualVMの拡張版であるGraalVM VisualVMに追加された。NodeJS、Ruby、LLVMのランタイムもアップグレードされている。GraalVM言語用Language Server Protocol実装のテクニカルプレビューがGraalVM VSCode Extensionで利用可能だ。
JDK 8およびJDK 11ベースのGraalVMディストリビューションは、いずれもGitHubからダウンロードすることができる。