JVMメモリとガベージコレクションシリーズ:実行エンジン

実行エンジン

実行エンジンの概要

実行エンジンは、インタプリタ、ジャストインタイムコンパイラ、ガベージコレクタを含むJVMの下位層に属しています。

画像-20200710080707873

実行エンジンは、Java仮想マシンのコアコンポーネントの1つです。「仮想マシン」は「物理マシン」に関連する概念です。どちらのマシンにもコード実行機能があります。違いは、物理マシンの実行エンジンがプロセッサ、キャッシュ、命令セット、およびオペレーティングシステムレベルで直接構築されていることです。また、仮想マシンの実行エンジンはソフトウェア自体で実現しているため、物理的な条件にとらわれることなく、命令セットと実行エンジンの構造をカスタマイズでき、直接サポートされていない命令セット形式を実行できます。ハードウェア。

JVMの主なタスクはバイトコードをロードすることですが、バイトコード命令はローカルマシン命令と同等ではないため、バイトコードをオペレーティングシステムで直接実行することはできません。バイトコード命令、シンボルテーブル、およびJVM。

画像-20200710081118053

次に、Javaプログラムを実行する場合、実行エンジンのタスクは、バイトコード命令を対応するプラットフォーム上のネイティブマシン命令に解釈/コンパイルすることです。簡単に言えば、JVMの実行エンジンは、高級言語を機械語に翻訳するトランスレーターとして機能します。

画像-20200710081259276

実行エンジンのワークフロー

  • 実行プロセス中に実行エンジンが実行する必要のあるバイトコード命令は、PCレジスタに完全に依存します。
  • 命令演算が実行されるたびに、PCレジスタは次に実行する必要のある命令のアドレスを更新します。
  • もちろん、メソッドの実行中に、実行エンジンは、ローカル変数テーブルに格納されているオブジェクト参照を介してJavaヒープ領域に格納されているオブジェクトインスタンス情報を正確に特定し、オブジェクトヘッダーのメタデータポインタを介してターゲットオブジェクトを特定できます。タイプ情報。

画像-20200710081627217

外観の観点からは、すべてのJava仮想マシンの実行エンジンの入力と出力は同じです。入力はバイトコードバイナリストリームであり、処理プロセスはバイトコードの分析と実行と同等のプロセスであり、出力は実行プロセス。

Javaコードのコンパイルと実行プロセス

ほとんどのプログラムコードを物理マシンのターゲットコードまたは仮想マシンが実行できる命令セットに変換する前に、上図のさまざまな手順を実行する必要があります。

  • 前のオレンジ色の部分は、JVMとは関係のないバイトコードファイルを生成するプロセスです。
  • 背後にある青と緑は、JVMが考慮する必要のあるプロセスです。

画像-20200710082141643

Javaコードのコンパイルは、Javaソースコードコンパイラによって実行されます。フローチャートは次のとおりです。

画像-20200710082433146

Javaバイトコードの実行はJVM実行エンジンによって完了します。フローチャートを以下に示します。

画像-20200710083036258

通訳とコンパイラについて話すために全体像を使用します

画像-20200710083656277

通訳とは

Java仮想マシンが起動すると、バイトコードは事前定義された仕様に従って1行ずつ解釈され、各バイトコードファイルの内容は、実行のために対応するプラットフォームのローカルマシン命令に「変換」されます。

ITコンパイラとは

JIT(Just In Timeコンパイラ)コンパイラ:仮想マシンは、ソースコードをローカルマシンプラットフォームに関連するマシン言語に直接コンパイルします。

Javaが半コンパイルおよび半解釈された言語である理由

JDK1.eの時代では、Java言語を「解釈と実行」として位置付ける方が正確です。その後、Javaは、ネイティブコードを直接生成できるコンパイラも開発しました。これで、JVMがJavaコードを実行すると、通常、解釈と実行がコンパイルと実行と組み合わされます。

ローカルコードを変換した後、キャッシュ操作を実行してメソッド領域に保存できます

機械語、命令、アセンブリ言語

機械語

バイナリエンコーディングで表現されたさまざまな命令は、マシン命令コードと呼ばれます。当初、人々はそれを使って機械語であるプログラムを書きました。

機械語はコンピュータで理解して受け入れることができますが、人の言語とはあまりにも異なり、人が理解して覚えるのは簡単ではなく、それを使ったプログラミングはエラーが発生しやすくなります。

それで書かれたプログラムがコンピュータに入力されると、CPUはそれを直接読み取って実行するので、他の言語で書かれたプログラムと比較して最速の実行速度を持っています。

マシン命令はCPUと密接に関連しているため、さまざまなタイプのCPUがさまざまなマシン命令に対応します。

命令

機械語は0と1で構成されるバイナリシーケンスであるため、読みやすさが非常に悪いため、人々は命令を発明しました。

命令は、機械語の0と1の特定のシーケンスを、少し読みやすい対応する命令(通常、英語ではmov、incなどと省略されます)に簡略化することです。

異なるハードウェアプラットフォームが同じ操作を実行するため、対応するマシンコードが異なる場合があります。したがって、異なるハードウェアプラットフォーム上の同じ命令(movなど)のマシンコードが異なる場合があります。

指図書

異なるハードウェアプラットフォームでサポートされる命令は異なります。したがって、各プラットフォームでサポートされている命令は、対応するプラットフォームの命令セットと呼ばれます。
一般的に

  • x86アーキテクチャプラットフォームに対応するx86命令セット
  • ARMアーキテクチャのプラットフォームに対応するARM命令セット

アセンブリ言語

指示の読みやすさがまだ不十分であるため、人々はアセンブリ言語を発明しました。

アセンブリ言語では、ニーモニックを使用してマシン命令のオペコードを置き換え、アドレスシンボル(Symbo1)またはラベル(Labe1)を使用して命令またはオペランドのアドレスを置き換えます。さまざまなハードウェアプラットフォームでは、アセンブリ言語はさまざまな機械語命令セットに対応しており、アセンブリプロセスを通じて機械語命令に変換されます。

コンピュータは命令コードしか認識しないため、アセンブリ言語で記述されたプログラムも、コンピュータが認識して実行する前に、マシンの命令コードに変換する必要があります。

高水準言語

コンピューターユーザーがプログラミングしやすくするために、後でさまざまな高級コンピューター言語が登場しました。

高水準言語は、機械語やアセンブリ言語よりも人間の言語に近いです。コンピューターが高水準言語で書かれたプログラムを実行する場合でも、プログラムを解釈して機械語にコンパイルする必要があります。このプロセスを完了するプログラムは、インタープリターまたはコンパイラーと呼ばれます。

[外部リンク画像の転送に失敗しました。ソースサイトにヒル防止リンクメカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします(img-CUQ0wbMr-1606810235780)(https://tuchuang666.oss-cn-shenzhen。 aliyuncs.com/img/image -20200710085323733.png)]

高水準言語は、機械命令に直接翻訳されるのではなく、以下で説明するように、CやC ++などのアセンブリ言語に翻訳されます。

C、C ++ソースプログラム実行プロセス

コンパイルプロセスは、コンパイルとアセンブリの2つの段階に分けることができます。

コンパイルプロセス:ソースプログラム(文字ストリーム)を読み取り、字句解析と文法を分析し、高級言語命令を機能的に同等のアセンブリコードに変換します

アセンブリプロセス:実際には、アセンブリ言語コードをターゲットマシン命令に変換するプロセスを指します。

画像-20200710085553258

バイトコード

バイトコードは、中間状態(中間コード)のバイナリコード(ファイル)です。マシンコードよりも抽象的であり、マシンコードになる前にインタプリタで変換する必要があります。

バイトコードは、主に特定のソフトウェア操作とソフトウェア環境を実現するためのものであり、ハードウェア環境とは関係ありません。

バイトコードの実装は、コンパイラと仮想マシンを介して行われます。コンパイラはソースコードをバイトコードにコンパイルし、特定のプラットフォーム上の仮想マシンはバイトコードを直接実行できる命令に変換します。

  • バイトコードの一般的なアプリケーションは次のとおりです。Javaバイトコード

通訳

JVM設計者の当初の意図は、Javaプログラムのクロスプラットフォーム機能を満たすことでした。そのため、静的コンパイルを使用してローカルマシン命令を直接生成することを避け、行ごとに使用するインタープリターの実装を生み出しました。実行時のバイトコード実行プログラムの解釈。

画像-20200710090203674

Javaソースファイルが直接JMVに変換されず、バイトコードファイルに変換されるのはなぜですか?直接翻訳されたコードが比較的大きいためか

インタプリタの実際の役割は、実行時の「トランスレータ」です。これは、バイトコードファイルの内容を、実行のために対応するプラットフォームのローカルマシン命令に「変換」します。

バイトコード命令が解釈されて実行された後、実行する必要のあるPCレジスタに記録されている次のバイトコード命令に従って解釈操作が実行されます。

通訳者の分類

Javaの開発履歴には、2セットの解釈エグゼキュータがあります。つまり、古代のバイトコードインタプリタと現在一般的に使用されているテンプレートインタプリタです。

バイトコードインタープリターは、実行中に純粋なソフトウェアコードを介してバイトコードの実行をシミュレートしますが、これは非常に非効率的です。

テンプレートインタープリターは各バイトコードをテンプレート関数に関連付け、テンプレート関数はバイトコードの実行時にマシンコードを直接生成するため、インタープリターのパフォーマンスが大幅に向上します。

HotSpot VMでは、インタプリタは主にインタプリタモジュールとコードモジュールで構成されています。

  • インタプリタモジュール:インタプリタのコア機能を実装します
  • コードモジュール:実行時にHotSpotVMによって生成されたローカルマシン命令を管理するために使用されます

現状

インタプリタは設計と実装が非常に単純であるため、Java言語に加えて、Python、Per1、Rubyなどのインタプリタに基づいて実行される多くの高級言語があります。しかし、今日、インタプリタベースの実行は非効率性の代名詞になり、一部のC / C ++プログラマーによって嘲笑されることがよくあります。

この問題を解決するために、JVMプラットフォームはジャストインタイムコンパイルと呼ばれるテクノロジーをサポートしています。ジャストインタイムコンパイルの目的は、関数が解釈および実行されないようにすることですが、関数本体全体をマシンコードにコンパイルすることです。関数が実行されるたびに、コンパイルされたマシンコードのみが実行されます。この方法は大幅に可能です。実行効率を向上させます。

ただし、インタプリタに基づく実行モードは、中間言語の開発に消えることのない貢献をします。

JITコンパイラ

Javaコード実行分類

1つ目は、ソースコードをバイトコードファイルにコンパイルしてから、インタープリターを使用して、実行時にバイトコードファイルをマシンコード実行に変換することです。

2つ目は、コンパイルして実行することです(マシンコードに直接コンパイルします)。実行効率を向上させるために、最新の仮想マシンは、ジャストインタイムコンパイルテクノロジ(JIT、ジャストインタイム)を使用して、実行前にメソッドをマシンコードにコンパイルします。

HotSpot VMは、現在市場に出回っている高性能仮想マシンの傑作の1つです。インタプリタとジャストインタイムコンパイラが共存するアーキテクチャを使用しています。Java仮想マシンが実行されているとき、インタープリターとジャストインタイムコンパイラーは互いに協力し合い、お互いの長所から学び、ネイティブコードとジャストインタイムコンパイラーをコンパイルする時間を比較検討するための最も適切な方法を選択しようとします。コードを直接解釈して実行する時間。

今日、Javaプログラムの実行パフォーマンスは長い間生まれ変わり、C / C ++プログラムと競合できるようになりました。

ここに問題があります

JITコンパイラはすでにHotSpotVMに組み込まれているのに、なぜインタプリタを使用してプログラムの実行パフォーマンスを「ドラッグ」する必要があるのでしょうか。たとえば、JRockit VMにはインタプリタが含まれておらず、すべてのバイトコードはジャストインタイムコンパイラによってコンパイルおよび実行されます。

  • JRockit仮想マシンはインタープリターを遮断します。つまり、ジャストインタイムコンパイラーのみを使用します。これは、JRockitがサーバーにのみデプロイされているためです。通常、JRockitは命令をコンパイルする時間があります。応答は要求されません。タイムリーなコンパイラーのコンパイルが完了すると、パフォーマンスが向上します。

まず、明確にしておきます。
プログラムが開始されると、インタプリタはすぐに有効になり、コンパイルの時間を節約してすぐに実行できます。
コンパイラが機能するためには、コードをローカルコードにコンパイルするのにある程度の実行時間がかかります。しかし、ネイティブコードにコンパイルした後は、実行効率が高くなります。

したがって
、JRockit VMでのプログラムの実行パフォーマンスは非常に効率的ですが、プログラムの起動時にコンパイルに時間がかかることは避けられません。サーバーサイドアプリケーションの場合、起動時間は注目されませんが、起動時間を重視するアプリケーションシナリオでは、バランスと引き換えにインタプリタとジャストインタイムコンパイラが共存するアーキテクチャを採用する必要がある場合があります。 。

このモードでは、Java仮想化機能が開始されると、実行前にジャストインタイムコンパイラがコンパイルを完了するのを待つ代わりに、インタプリタが最初に役割を果たすことができます。これにより、不要なコンパイル時間を大幅に節約できます。時間が経つにつれて、コンパイラが機能し、ますます多くのコードをローカルコードにコンパイルし、より高い実行効率を獲得します。

同時に、コンパイラの根本的な最適化が失敗した場合、解釈と実行はコンパイラの「エスケープドア」として機能します。

HotSpotJVM実行方法

仮想マシンが起動すると、ジャストインタイムコンパイラがコンパイルを完了してから実行するのを待たずに、インタプリタが最初に動作できるため、不要なコンパイル時間を大幅に節約できます。また、プログラムの実行時間の経過とともに、ジャストインタイムコンパイラが徐々に機能し、ホットスポット検出機能に従って、プログラムの実行効率を高める代わりに、貴重なバイトコードがローカルマシン命令にコンパイルされます。

ケーススタディ

オンライン環境での解釈と実行、およびコンパイルと実行の間の微妙な弁証法的関係に注意してください。暖かい状態で機械が耐えることができる負荷は、冷たい状態よりも大きくなります。ウォーム状態のトラフィックでフローが切断された場合、トラフィックを伝送できないため、コールド状態のサーバーが中断される可能性があります。

本番環境のリリースプロセスでは、リリースはバッチで実行され、マシンの数に応じて複数のバッチに分割されます。各バッチのマシンの数は、クラスター全体の最大1/8を占めます。このような失敗事例がありました。プログラマーがリリースプラットフォームでバッチでリリースし、リリースバッチの総数を入力するときに、誤って2つのリリースとして成分を入力しました。ホット状態の場合、通常の状況では、マシンの半分はほとんどトラフィックを伝送できませんが、起動したばかりのJVMが解釈および実行されるため、ホットコード統計とJIT動的コンパイルが実行されず、現在の1 /になります。マシン起動後2リリース。すべてのサーバーがすぐにダウンしました。この障害は、JITの存在を示しています。—アリチーム

画像-20200710095417462

コンセプト説明

  • Java言語の「コンパイル期間」は、実際には「不確実な」操作の期間です。これは、.javaファイルを.classファイルに変換するフロントエンドコンパイラ(実際には「コンパイラフロントエンド」と呼ばれる方が正確です)を指す場合があるためです。プロセス;仮想マシンのバックエンドランタイムコンパイラ(JITコンパイラ、ジャストインタイムコンパイラ)を指す場合もあります。
  • バイトコードをマシンコードに変換するプロセス。
  • また、静的事前コンパイラ(Ahead of Timeコンパイラ)を使用して.javaファイルをローカルマシンコードに直接コンパイルするプロセスを指す場合もあります。

フロントエンドコンパイラ:SunのJavac、Eclipse JDTのインクリメンタルコンパイラ(ECJ)。

JITコンパイラ:HotSpotVMのC1およびC2コンパイラ。

AOTコンパイラ:Java用GNUコンパイラ(GCJ)、ExcelsiorJET。

ホットスポット検出技術

複数回呼び出されるメソッド、またはメソッド本体に多数のループがあるループ本体は「ホットコード」と呼ばれるため、JITコンパイラでローカルマシン命令にコンパイルできます。このコンパイルメソッドはメソッドの実行中に発生するため、オンスタック置換、または略してOSR(オンスタック置換)コンパイルと呼ばれます。

この標準に到達するには、メソッドを何回呼び出す必要がありますか、それともループ本体を何回実行する必要がありますか?JITコンパイラがこれらの「ホットコード」をローカルマシン命令にコンパイルして実行する前に、明確なしきい値が必要です。ここでは主にホットスポット検出機能に依存します。

HotSpot VMで現在使用されているホットスポット検出方法は、カウンターベースのホットスポット検出です。

HotSpot Vは、カウンターベースのホットスポット検出を使用して、メソッドごとに2つの異なるタイプのカウンター、つまり呼び出しカウンターとバックエッジカウンターを作成します。

  • メソッド呼び出しカウンターは、メソッド呼び出しの数をカウントするために使用されます
  • エッジリターンカウンタは、ループ本体によって実行されたループの数をカウントするために使用されます

メソッド呼び出しカウンター

このカウンターは、メソッドが呼び出された回数をカウントするために使用されます。デフォルトのしきい値は、クライアントモードで1500回、サーバーモードで10000回です。このしきい値を超えると、JITコンパイルがトリガーされます。

このしきい値は、仮想マシンのパラメーター-XX:CompileThresholdを使用して手動で設定できます。

メソッドが呼び出されると、最初にメソッドにJITコンパイルされたバージョンがあるかどうかがチェックされます。存在する場合は、コンパイルされたネイティブコードが最初に使用されます。コンパイルされたバージョンがない場合は、メソッド呼び出しカウンター値に1を加算してから、メソッド呼び出しカウンターと戻りカウンター値の合計がメソッド呼び出しカウンターのしきい値を超えているかどうかを判別します。しきい値を超えた場合、このメソッドのコードコンパイル要求がジャストインタイムコンパイラに送信されます。

画像-20200710101829934

ホットスポットの減衰

何も設定されていない場合、メソッド呼び出しカウンターは、メソッド呼び出しの絶対数ではなく、相対的な実行頻度、つまり一定期間内のメソッド呼び出しの数をカウントします。一定の制限時間を超えても、メソッド呼び出しの数がジャストインタイムコンパイラに送信されてコンパイルされるのに十分でない場合、このメソッドの呼び出しカウンタは半分になります。このプロセスが呼び出されます。メソッド呼び出しカウンターのカウンター減衰。、そしてこの期間はカウンター半減期時間(カウンター半減期時間)と呼ばれます。

  • 半減期は化学の概念です。たとえば、発掘された文化的遺物の年齢は、C60をチェックすることで取得できます。

熱減衰のアクションは、仮想マシンがガベージを収集しているときに実行されます
仮想マシンパラメータ-XXを使用できます。-UseCounterDecayを使用して、熱減衰をオフにし、メソッドカウンタにメソッド呼び出しの絶対数をカウントさせます。ちなみに、システムが十分に長く実行されている限り、ほとんどのメソッドはローカルコードにコンパイルされます。

さらに、-XX:CounterHalfLifeTimeパラメーターを使用して、半減期のサイクル時間を秒単位で設定できます。

バックツーエッジカウンター

その機能は、メソッド内のループ本体コードの実行回数をカウントすることであり、制御フローがバイトコード内で後方にジャンプする命令は「バックエッジ」と呼ばれます。明らかに、バックサイドカウンタ統計を確立する目的は、OSRコンパイルをトリガーすることです。

画像-20200710103103869

HotSpotVMはプログラムの実行方法を設定できます

デフォルトでは、HotSpot VMは、インタープリターとジャストインタイムコンパイラーが共存するアーキテクチャを採用しています。もちろん、開発者は、実行時にインタープリターを使用するかどうかを特定のアプリケーションシナリオに従って、コマンドを使用してJava仮想マシンを明示的に指定できます。 。完全にジャストインタイムコンパイラで実行されます。次のように:

  • -Xint:プログラムを完全にインタプリタモードで実行します。
  • -Xcomp:プログラムは完全なジャストインタイムコンパイラモードで実行されます。ジャストインタイムコンパイルに問題がある場合、インタプリタが実行に介入します
  • -Xmixed:プログラムを一緒に実行するために、インタープリターとジャストインタイムコンパイラの混合モードを採用します。

画像-20200710103340273

HotSpotVMでのJIT分類

JITコンパイラもC1とC2の2つのタイプに分けられます。HotSpotVMにはクライアントコンパイラとサーバーコンパイラの2つのJITコンパイラが組み込まれていますが、ほとんどの場合、C1コンパイラと略してC2と呼びます。開発者は、以下に示すように、次のコマンドを使用して、Java仮想マシンが実行時に使用するジャストインタイムコンパイラの種類を明示的に指定できます。

  • -client:クライアントモードで実行するJava仮想マシンを指定し、C1コンパイラを使用します。

    • C1コンパイラは、バイトコードに対して単純で信頼性の高い最適化を実行しますが、これには短時間かかります。より速いコンパイル速度を達成するために。
  • -server:サーバーモードで実行するJava仮想マシンを指定し、C2コンパイラを使用します。

    • C2は、時間のかかる最適化と根本的な最適化を実行します。ただし、最適化されたコード実行の方が効率的です。(C ++を使用)

C1およびC2コンパイラのさまざまな最適化戦略

コンパイラごとに異なる最適化戦略があります。C1コンパイラには、主にインライン化、仮想化解除、および余剰の除去のための方法があります。

  • メソッドのインライン化:参照される関数コードを参照ポイントにコンパイルします。これにより、スタックフレームの生成を減らし、パラメーターの受け渡しとジャンププロセスを減らすことができます。
  • 非仮想化:唯一の実装ファンをインライン化
  • 冗長性の排除:操作中に実行されないコードを折りたたむ

C2の最適化は主にグローバルレベルで行われ、エスケープ分析が最適化の基礎となります。エスケープ分析に基づくC2にはいくつかの最適化があります。

  • スカラー置換:集計オブジェクトの属性値をスカラー値に置き換えます
  • スタックへの割り当て:エスケープされていないオブジェクトの場合、オブジェクトはヒープではなくスタックに割り当てられます
  • 同期の排除:同期操作をクリアします。通常は同期と呼ばれます

階層化されたコンパイル戦略

階層型コンパイル(階層型コンパイル)戦略:プログラムの解釈と実行(パフォーマンス監視を有効にしない)により、C1コンパイルをトリガーし、バイトコードをマシンコードにコンパイルできます。単純な最適化を実行するか、パフォーマンス監視を追加できます。C2コンパイルはパフォーマンスに基づいて行われます。監視。情報は根本的に最適化されています。

ただし、Java7バージョン以降、開発者がプロ​​グラムでコマンド「-server」を明示的に指定すると、階層コンパイル戦略がデフォルトでオンになり、C1コンパイラとC2コンパイラが相互に連携してコンパイルタスクを実行します。

総括する

  • 一般的に、JITによってコンパイルされたマシンコードのパフォーマンスは、インタプリタのパフォーマンスよりも優れています。
  • C2コンパイラの起動時間はC1よりも遅いです。システムが安定して実行された後、C2コンパイラの実行速度はC1コンパイラの実行速度よりもはるかに速くなります。

AOTコンパイラ

jdk9はAoTコンパイラー(静的事前コンパイラー、事前コンパイラー)を導入しました

Java 9では、実験的なAOTコンパイルツールaotcが導入されました。Graalコンパイラを使用して、入力Javaクラスファイルをマシンコードに変換し、生成された動的共有ライブラリに格納します。

いわゆるAOTコンパイルは、ジャストインタイムコンパイルとは逆の概念です。ジャストインタイムコンパイルとは、プログラムの実行中にハードウェア上で直接実行できるマシンコードにバイトコードを変換し、それをホスティング環境にデプロイするプロセスを指します。AOTコンパイルとは、プログラムが実行される前にバイトコードをマシンコードに変換するプロセスを指します。

.java -> .class -> (使用jaotc) -> .so

最大の利点:Java仮想マシンのロードはバイナリライブラリにプリコンパイルされており、直接実行できます。コンパイラが時間内にウォームアップするのを待つ必要はありません。これにより、Javaアプリケーションの「最初の実行が遅くなる」という悪い経験が減ります。

短所:

  • Java「一度コンパイルしてどこでも実行」を破棄します。異なるハードウェアとOSごとに対応するリリースパッケージをコンパイルする必要があります
  • Javaリンクプロセスの動的な性質が低下し、ロードされたコードはすべてコンパイラで認識されている必要があります。
  • 引き続き最適化を続ける必要があり、最初はLinux X64Javaベースのみをサポートします

最後まで書く

  • JDK10以降、HotSpotは新しいジャストインタイムコンパイラを追加しました:Graalコンパイラ
  • コンパイル効果G2コンパイラはわずか数年でレビューされており、将来が期待できます
  • 現在、実験ステータスラベルでは、使用する前にスイッチパラメータを使用してアクティブにする必要があります
-XX:+UnlockExperimentalvMOptions -XX:+UseJVMCICompiler

おすすめ

転載: blog.csdn.net/weixin_43314519/article/details/110437956