Graalコンパイラ
Graalコンパイラは動的なJust-in-Time (JIT)コンパイラで、Javaで記述され、バイトコードをマシン・コードに変換します。Graalコンパイラは、互換性のあるバージョンのJVMコンパイラ・インタフェース(JVMCI)をサポートするJava HotSpot VMと統合されています。JVMCIはJVMに対する権限のある低レベルのインタフェースであり、Javaで記述されたコンパイラをJVMで動的なコンパイラとして使用できるようにします(「JEP 243」を参照)。これにより、メソッドのバイトコードなどのメタデータをVMから読み取り、マシン・コードをVMにインストールできます。GraalVMには、JVMCIをサポートするHotSpot JVMのバージョンが含まれています。
コンパイラの利点
Graalコンパイラは、コード分析および最適化に対する独自のアプローチによって、JVMで実行されているプログラムのパフォーマンスを最適化します。これには、積極的なインライン化、多相インライン化など、複数の最適化アルゴリズム(フェーズと呼ばれる)が含まれています。GraalVM Community Editionのプラットフォームに依存しないコンパイラの最適化については、こちらを参照してください。
Graalコンパイラは、高度に抽象化されたプログラムのパフォーマンスの利点をもたらすことができます。たとえば、特定のオブジェクトのコストのかかる割当てを削除できる部分エスケープ分析の最適化が含まれています。この最適化は、新しいオブジェクトがコンパイル単位の外部からアクセス可能になるタイミングを決定し、コンパイル単位を「エスケープ」するパス(オブジェクトがパラメータとして渡されるか、フィールドに格納されるか、メソッドから戻されるなど)でのみ割当てを行います。これにより、ヒープ割当ての数を削減することで、アプリケーションのパフォーマンスを大幅に向上できます。StreamやLambdaなど、より最新のJava機能を使用するコードでは、速度が大幅に向上します。この種のコードには、非エスケープまたは部分エスケープ・オブジェクトが多数含まれるためです。I/Oやメモリー割当てなど、コンパイラで削除できないのものにバインドされているコードは、あまり改善されません。パフォーマンス・チューニングの詳細は、「JVMでのコンパイラ構成」を参照してください。
グラフ・コンパイル
ゲスト・プログラミング言語、つまりJavaScript、Ruby、R、PythonおよびWebAssemblyをホストJVMベース言語と同じランタイムで実行するには、コンパイラは、ソース言語と生成されるマシン・コードの間で言語に依存しない中間表現(IR)を使用する必要があります。このロールにはグラフが選択されました。
グラフは、if文やループなど、異なる言語の類似した文を同じ方法で表すことができるため、同じプログラム内で言語を混在させることができます。これにより、Graalコンパイラは、言語に依存しない最適化を実行し、このグラフにマシン・コードを生成できます。
GraalVMには、GraalVMで実行される、プログラミング言語のインタプリタをビルドするためのJavaで記述されたライブラリ(Truffle言語実装フレームワーク)も含まれています。このため、これらの言語は、Graalコンパイラの最適化の可能性のメリットを得ることができます。このようなコンパイルのパイプラインは次のとおりです:
- Truffleフレームワークのコードおよびデータ(抽象構文ツリー)が部分的に評価され、コンパイル・グラフが生成されます。このような抽象構文ツリー(AST)がホットな(コール回数が多い)場合は、コンパイラによるコンパイルがスケジュールされます。
- マシン・コードを生成するために、Graalコンパイラによってコンパイル・グラフが最適化されます。
- JVMCIは、このマシン・コードをVMのコード・キャッシュにインストールします。
- ASTは、インストールされたマシン・コードが使用可能になると、そのコードに実行を自動的にリダイレクトします。
Ahead-of-Timeコンパイル
Truffleフレームワークに加えて、GraalVMでは、その最適化コンパイラが高度なAhead-of-Time (AOT)コンパイル・テクノロジであるネイティブ・イメージに組み込まれています。これは、JavaおよびJVMベースのコードをネイティブ・プラットフォームの実行可能ファイルに変換します。これらのネイティブ実行可能ファイルは、ほぼ瞬時に起動し、より小さく、かつ同じJavaアプリケーションのリソースの消費がより少ないため、クラウド・デプロイメントおよびマイクロサービスに最適です。AOTコンパイルの詳細は、「ネイティブ・イメージ」を参照してください。
コンパイラのオペレーティング・モード
HotSpot JITコンパイラとして使用する場合、Graalコンパイラには、プリコンパイル済マシン・コード(libgraal)または動的に実行されるJavaバイトコード(jargraal)の2つのオペレーティング・モードがあります。
libgraal: Graalコンパイラは、事前にネイティブ共有ライブラリにコンパイルされます。このオペレーティング・モードでは、共有ライブラリはHotSpot VMによってロードされます。コンパイラは、HotSpotヒープとは別のメモリーを使用します。ウォームアップする必要がないため、最初から高速で実行されます。これはデフォルトの推奨操作モードです。
jargraal: Graalコンパイラは、Javaアプリケーションのそれ以外の部分と同じウォームアップ・フェーズを経由します。つまり、ホット・メソッドがコンパイルされる前に最初に解釈されます。このモードは、-XX:-UseJVMCINativeLibrary
コマンドライン・オプションを使用して選択します。
診断データ
捕捉されない例外がコンパイラによってスローされた場合、コンパイルは単に破棄され、実行が続行します。Graalコンパイラは、かわりに、バグ・レポートとともに送信できる診断データ(即時表示グラフなど)を生成できます。これは、-Dgraal.CompilationFailureAction=Diagnose
を使用して有効にします。診断出力のデフォルトの場所は、プロセスの現在の作業ディレクトリの下のgraal_dumps/
にありますが、-Dgraal.DumpPath
オプションを使用して変更できます。VMのシャットダウン中に、診断データを含むアーカイブの場所がコンソールに出力されます。
さらに、-Dgraal.Dump
オプションを使用して、Graalコンパイラによって実行されたコンパイルの診断データを生成できます。これにより、コンパイラによってコンパイルされたすべてのメソッドの診断データが生成されます。診断データが生成される一連のメソッドを絞り込むには、-Dgraal.MethodFilter=<class>.<method>
オプションを使用します。たとえば、-Dgraal.MethodFilter=java.lang.String.*,HashMap.get
では、java.lang.String
クラスのメソッド、および非修飾名がHashMap
であるクラスのget
というメソッドの診断データのみが生成されます。
診断データは、ファイルに書き込むかわりに、ネットワークを介してIdeal Graph Visualizerに送信することもできます。これには、-Dgraal.PrintGraph=Network
オプションが必要です。これにより、コンパイラが診断データを127.0.0.1:4445に送信しようとします。このネットワーク・エンドポイントは、-Dgraal.PrintGraphHost
および-Dgraal.PrintGraphPort
オプションを使用して構成できます。