JIT コンパイラーによるコードの最適化方法
コンパイルするメソッドが選択されると、JVM はバイトコードを Just-In-Time (JIT) コンパイラーに送ります。 メソッドを正確にコンパイルするために、JIT はバイトコードの意味構造と構文を理解する必要があります。
JIT コンパイラーがメソッドを分析するのを支援するために、バイトコードは最初にツリー と呼ばれる内部表現に再公式化されます。この表現はバイトコードよりマシン・コードに類似しています。 次に、メソッドのツリーに対して分析と最適化が実行されます。 最後に、ツリーがネイティブ・コードに変換されます。 このセクションの残りの部分では、JIT コンパイルの各フェーズを簡単に概説します。 詳しくは、 JIT または AOT の問題の診断を参照してください。
JIT コンパイラーは、複数のコンパイル・スレッドを使用して、JIT コンパイル・タスクを実行できます。 複数のスレッドを使用すると、Java アプリケーションをより迅速に開始するのに役立つ可能性があります。 実際には、システムに未使用の処理コアがある場合にのみ、複数の JIT コンパイル・スレッドでパフォーマンスが向上します。
コンパイルは以下のフェーズで構成されます。 ネイティブ・コードの生成以外のすべてのフェーズは、クロスプラットフォーム・コードです。
フェーズ 1 - インライン化
- 小規模なインライン化
- 呼び出しグラフのインライン化
- テール再帰の除去
- 仮想呼び出し保護の最適化
フェーズ 2 - ローカル最適化
- データ・フローのローカルでの分析および最適化
- レジスター使用の最適化
- Java イディオムの単純化
フェーズ 3 - 制御フローの最適化
- コードの再配列、分割、および削除
- ループの縮小および反転
- ループのストライドおよびループ不変コードの動作
- ループのアンロールおよびピーリング
- ループのバージョン管理および専門化
- 例外を対象とした最適化
- スイッチ分析
フェーズ 4 - 全体的な最適化
- データ・フローの全体的な分析および最適化
- 部分的な冗長性の除去
- エスケープ分析
- GC およびメモリー割り振りの最適化
- 同期の最適化
フェーズ 5 - ネイティブ・コードの生成
ネイティブ・コードの生成は、プラットフォーム・アーキテクチャーによって異なります。 一般に、コンパイルのこのフェーズ中は、メソッドからなるツリーがマシン・コード命令に変換され、アーキテクチャーの特徴に沿っていくつかの小規模な最適化が実行されます。 コンパイルされたコードは JVM プロセス・スペース (コード・キャッシュ と呼ばれる) の一部に入れられます。将来メソッドに対する呼び出しがコンパイル済みのコードを呼び出すように、コード・キャッシュにおけるメソッドの場所が記録されます。 JVM プロセスは常に、JVM 実行可能ファイルと、JVM 内のバイトコード・インタープリターに動的にリンクされている一連の JIT コンパイルされたコードで構成されています。