実行エンジン、エスケープ分析、JITジャストインタイムコンパイル

実行エンジンとは何ですか?

実行エンジンはJVMのサブシステムのセットです。プログラムが実行されると、実行エンジンが動作を開始します。仮想マシンのバイトコード実行エンジンの概念モデルはJava仮想マシン仕様で定式化され、この概念モデルはさまざまな仮想マシンの実行になります。エンジンの均一な外観(ファサード)。異なる仮想マシンの実装では、実行エンジンがJavaコードを実行する場合、解釈された実行(インタープリターによって実行される)とコンパイルされた実行(ジャストインタイムコンパイラによって生成されるローカルコードの実行)、またはその両方の2つの方法があります。 、およびいくつかの異なるレベルのコンパイラ実行エンジンが含まれる場合もあります。ただし、外観の観点からは、すべてのJava仮想マシンの実行エンジンは同じです。入力はバイトコードファイルであり、処理プロセスはバイトコード分析と同等のプロセスであり、出力は実行結果です。

java用の2つのインタープリター

javaに2種類のインタープリターがあるのはなぜですか?javaの初期には、javaにはバイトコードインタープリターしかありませんでしたが、バイトコードインタープリターはC ++プログラムによって完成されました。つまり、作成するプログラムは、コンピューターによって認識されるためにC ++によって生成される必要があります。ハードコーディングされており、動作効率が非常に低いとは言えません。確かに、高効率でハードコーディングされて直接生成されるわけではありません。このバイトコードインタープリターモードでは、多くのC ++プログラマーが実際にJAVAプログラマーを軽蔑しています。JAVAプログラマーが作成できます。コードでは、ハードコードされた解釈と実行を完了するためにC ++が必要です。実際、この状況は海外でより顕著です。そのため、C ++に変換せずに、自分で直接ハードコードにコンパイルできるかどうか疑問に思った大物もいます。ハードコーディングされているため、javaのテンプレートインタープリターは後で生まれました。現在のjavaまたはバイトコードインタープリターにデフォルトで使用されているテンプレートインタープリターです。実際、Javaはデフォルトでバイトコード+テンプレートインタープリターを使用しています。これについては、以下で説明します。

ジャストインタイムコンパイル

バイトコードインタープリター

javaバイトコード-> C ++コード->ハードコード(05 06 07)
など:

public class T0806 {
    
    

    public static void main(String[] args) {
    
    
        T0806_1 t = new T0806_1();
        System.out.println(t);
    }

}

class T0806_1{
    
    

    public T0806_1(){
    
    
        System.out.println("init ...");
    }
}

mainメソッドによって生成されるバイトコードは次のとおりです。

0 new #2 <com/bml/t0816/T0806_1>
 3 dup
 4 invokespecial #3 <com/bml/t0816/T0806_1.<init>>
 7 astore_1
 8 getstatic #4 <java/lang/System.out>
11 aload_1
12 invokevirtual #5 <java/io/PrintStream.println>
15 return

では、バイトコードインタープリターはC ++でどのように機能しますか?
実際、C ++は次のように実行を
説明します。forまたはwhileループである行ごとの説明:

whil(条件){
    
    
    char code = xxx;
    switch(code){
    
    
    case new:
        ....
    break;
    case dup:
        ....
        break;
    }
   
}

実際、コードの各行を読み取り、取得したバイトコード命令に従って対応する操作を実行します。ここでは、バイトコードインタープリターを使用して、openjdkの基になるコードのコードフラグメント(c ++)をインターセプトしました。

CASE(_new): {
    
    
        u2 index = Bytes::get_Java_u2(pc+1);
        ConstantPool* constants = istate->method()->constants();
        if (!constants->tag_at(index).is_unresolved_klass()) {
    
    
          // Make sure klass is initialized and doesn't have a finalizer
          Klass* entry = constants->slot_at(index).get_klass();
          assert(entry->is_klass(), "Should be resolved klass");
          Klass* k_entry = (Klass*) entry;
          assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
          InstanceKlass* ik = (InstanceKlass*) k_entry;
          if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
    
    
 ……

実際、原則は上記の例で説明されています。ここではopdnjdkソースコードを調査するためではなく、原則を明確にするためにソースコードを掲載しています。

テンプレートインタープリター

実行する必要があるのは次のとおりです。javaバイトコード->ハードコード
1.読み取り、書き込み、実行可能なメモリを申請します(Macの最下層はuninxですが、保護のために、MacはJITを実行できません。 macは実行可能なメモリスペースに適用できないと言われています)
。2。新しいキーワードまたはdupが見つかった場合、これはバイトコード命令であり、テンプレートインタープリターはnewまたはdupの対応するハードコードを直接取得します。
3.新しいバイトコード命令または他のバイトコード命令を処理するためのハードコードされた命令を、適用されたメモリスペースに直接書き込みます
。4。関数ポインタを適用し、この関数ポインタを使用してこのメ​​モリを実行します(関数ポインタ->関数を実行するポインタ) );
5.プログラムが呼び出されると、この関数のポインターを介して直接呼び出すことができます。

2つのインタープリター実行プロセス

ここに画像の説明を挿入
上の図は、2つのインタープリターをグラフで表したものです。前の説明との違いはグラフィックで表されています。バイトコードインタープリターの場合、ハードコードはC ++のコードを介して生成され、テンプレートインタープリターは直接ハードコードを生成します。

JITの3つの動作モード(ジャストインタイムコンパイル)

-Xint:純粋なバイトコードインタープリターモード
-Xcomp:純粋なテンプレートインタープリターモード
-Xmixed:バイトコードインタープリター+テンプレートインタープリターモード
では、jdkがデフォルトで使用するインタープリターの種類は何ですか?どのような通訳が効率的ですか?
実際、jdkはデフォルトで-Xmixedインタープリター(混合インタープリター)を使用します。バイトコードインタープリターとテンプレートインタープリターの両方があります。バイトコードインタープリターはプログラムの初期段階で使用され、テンプレートインタープリターは後の段階で使用されます。デバイスは実際にはパラメータによって制御されます。これについては後で説明します
。cmdで実行します。

$ java -version
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

jdkがサーバーモードで実行され、混合モードであるため、jvmはバイトコードインタープリター+テンプレートインタープリターで実行されます。
上記の3つのモードの中で、純粋なテンプレートインタープリターがより効率的に実行されます。しかし、核の混合はあまり変わらないが、プログラムが小さい場合、バイトコードは高速で、かつ純粋なテンプレートインタプリタプログラムが混在インタプリタが最高の全体的なパフォーマンスがあるので、非常に大きい場合に使用され、
あなたが非を使用する必要がある場合デフォルトのインタープリターの場合、対応するインタープリターパラメーターをスタートアップパラメーターに追加するだけです。

javaの2つのジャストインタイムコンパイラ

1.C1コンパイラ

-クライアントモードが開始され、C1コンパイラがデフォルトで開始されます。特徴は何ですか?
1.収集する必要のあるデータが少なくなります。つまり、ジャストインタイムコンパイルをトリガーするための条件が緩くなります
。2。組み込みのコンパイル最適化最適化ポイントが少なくなり
ます。3。コンパイル時間はC2よりもCPUに負担がかからず、結果はコンパイル後に生成されます。コード実行効率はC2よりも低い

2.C2コンパイラ

-サーバーモードが開始されます。特徴は何ですか?
1.収集するデータが増える
2.コンパイル中にCPUが非常に消費される
3.コンパイルの最適化には多くのポイントがあります
4.コンパイルによって生成されるコードは非常に効率的です

3.ハイブリッドコンパイル

現在のサーバーモードは、もはや純粋にC2を使用していません。プログラム運用開始時は、生成されるデータが少ないため、この時点でC1コンパイルを実行します。プログラムを一定時間実行した後、十分なデータを収集してC2コンパイラを実行します。

JITはMacでは利用できません!Macは読み取り可能、書き込み可能、​​実行可能なメモリブロックを適用できないため、
ジャストインタイムコンパイルによって生成されたコードがテンプレートコンパイラによって使用されます。

ジャストインタイムコンパイル開始トリガー条件

ジャストインタイムコンパイルの最小単位は、関数やメソッドではなく、コードブロック(whileなど)です。
コマンドを使用して、タイムリーなコンパイラの実行回数の構成を表示できます

java -XX:+PrintFlagsFinal -version|grep CompileThreshold
     intx CompileThreshold                          = 10000           {
    
    pd product}
    uintx IncreaseFirstTierCompileThresholdAt       = 50              {
    
    product}
     intx Tier2CompileThreshold                     = 0               {
    
    product}
     intx Tier3CompileThreshold                     = 2000            {
    
    product}
     intx Tier4CompileThreshold                     = 15000           {
    
    product}
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

クライアントコンパイラモードでは、CompileThresholdのデフォルト値は1500です。
サーバーコンパイラモードでは、CompileThresholdのデフォルト値は10000です。
これはjdkのデフォルト値です。jvmチューニング用にこのパラメータを調整できますが、特に注意する必要があります。あまり明確ではありません。したがって、デフォルト値を変更しないでください。

たとえば、このモードでは、コードブロックの1つがジャストインタイムコンパイルの条件下で実行されますが、長時間実行されない場合は、テンプレート解釈の条件が導入されます。これは熱減衰の概念であり、
非常に単純な例です。特定のアプリをアクティブ化したのはメンバーです。有効期限が切れた後に更新しないと、同じ理由でその成長値が減衰します。したがって、プログラムを3000回実行するように設定すると、テンプレートインタープリターを終了した後です。 、2倍の割合で減衰します。前の条件を達成するために、2倍の割合で実行されます。

ホットコードバッファ

ホットコードとは何ですか?ホットコードはハードコード、つまりテンプレートインタープリターによって生成されたハードコードです。テンプレートインタープリターを有効にすると、毎回ハードコードを生成することはできなくなり、キャッシュされます。キャッシュはどこにありますか? ?
ホットコードはメソッド領域にキャッシュされますが、メソッド領域ではオーバーフローしますか?oomは異常ですか?
実際には表示されません。内部には、redis、LRUの除去メカニズムと同様の除去メカニズムがあるため、OOMの例外はありません。
では、ホットコードキャッシュの大きさはどれくらいですか?jvmのデフォルトサイズは次のとおりです。
サーバーコンパイラモードのコードキャッシュサイズは
2496KBから始まりクライアントコンパイラモードのコードキャッシュサイズは160KBから始まります。
注:jdkのデフォルトは64未満のサーバーモードで、クライアントモードがない場合、クライアントモードは32ビットマシンで
のみ、java-clientでクライアントモードを有効にするよう指定します

$ java -XX:+PrintFlagsFinal -version|grep CodeCache
    uintx CodeCacheExpansionSize                    = 65536           {
    
    pd product}
    uintx CodeCacheMinimumFreeSpace                 = 512000          {
    
    product}
    uintx InitialCodeCacheSize                      = 25559042496k)         {
    
    pd product}
     bool PrintCodeCache                            = false           {
    
    product}
     bool PrintCodeCacheOnCompilation               = false           {
    
    product}
    uintx ReservedCodeCacheSize                     = 251658240       {
    
    pd product}
     bool UseCodeCacheFlushing                      = true            {
    
    product}
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

したがって、CodeCacheパラメータもチューニングの1つですが、システムアーキテクチャをチューニングする必要もあります。そうでない場合は、デフォルトを使用することをお勧めします。チューニングでは、次の2つのパラメータを調整でき
ます
。InitialCodeCacheSizeReservedCodeCacheSize

ジャストインタイムコンパイルはどのように機能しますか?

実際、ジャストインタイムコンパイルとGCはどちらもjvmの内部スレッドVM_THREADを使用し、内部でキューを維持します。リクエストスレッドがキューに参加すると、実行されます。ジャストインタイムコンパイルは非同期で実行されるため、ジャストインタイムコンパイルが実行されます。スレッドはいくつあり、それらを調整する方法は?

$ java -XX:+PrintFlagsFinal -version|grep CICompilerCount
     intx CICompilerCount                           = 2               {
    
    product}
     bool CICompilerCountPerCPU                     = true            {
    
    product}
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

CICompilerCountはこのパラメーターを調べます。つまり、ジャストインタイムコンパイルの内部スレッドは2つだけです。このパラメーターを使用して調整できます。これ
は、何年も前に銀行システムがホットとコールドを使用していたときに発生したケースです。モード、つまり、システムはホットマシンで実行されており、コールドマシンは災害復旧として使用されます。ホットマシンに問題が発生してコールドマシンルームに切り替えると、多数の要求が遅くなり、システムがスタックして、トランザクションの失敗率が発生します。非常に高いため、最後にシステムが直接ハングしました。
この問題を長い間チェックしましたが、問題は解決しませんでした。2台のマシンの構成はまったく同じで、プログラムの構成はまったく同じで、システムパラメータは最適に調整されていますが、チェックされていません。誤って後で確認しました。 JVMのホットコードキャッシュ領域を見ると、突然明らかになりました:
ホットエンジンが大量のホットコードをキャッシュしているため、大きな同時実行に耐えることができ、コールドエンジンはリクエストを実行していないため、突然大きな同時トランザクションが発生して実行されます低速で、同時実行性が特定のレベルに達すると、すぐに処理できなくなり、ダウンタイムが発生します。

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

1. Javacのコンパイル、javaの実行、
2。Bytecodeインタープリターの解釈と実行、テンプレートインタープリターのコンパイルと実行

エスケープ分析

エスケープ分析という単語は、理解するために2つの単語に分けることができます。エスケープ、分析(エスケープ分析はデフォルトで有効になっています)

どのような状況で逃げ道があり、どのような状況で分析する必要がありますか?
エスケープとは何ですか?
率直に言って、共有変数、戻り値、パラメータなどです。一言で言えば、この変数はローカルではありません。たとえば
、メソッドの戻り値です。メソッドが実行されると、戻り値はエスケープされ、制御する方法がありません。共有変数も意味があります。要約すると
、メソッドの外部と
スレッドの外部の2つの状況があります。
これら2つのエスケープが存在します。
非エスケープとは何ですか。
つまり、オブジェクトのスコープがローカルであるため、エスケープする方法はありません。
分析:
分析は技術的な方法です。なぜオブジェクトのエスケープを分析する必要があるのですか。 ?
それは、最適化する必要があるため、コードの実行を最適化する。
エスケープ分析に基づいて、JVMは3つの最適化技術を開発しました。

スカラー置換

スカラー:分割できません。javaの基本データタイプ(8の基本データタイプ)はスカラーです。
総量:分割できます。オブジェクト

class T0806_1{
    
    
     public static int x = 2;
     public static int y =3;
    public T0806_1(){
    
    
        System.out.println("init ...");
    }
}


class T2{
    
    
    public static void main(String[] args) {
    
    

        System.out.println(T0806_1.x);
        System.out.println(T0806_1.y);
    }
}

注意を払う

System.out.println(T0806_1.x);
System.out.println(T0806_1.y);

これらの2行のコードは、エスケープ分析で最適化されています。実行すると、実際に表示されるのは

System.out.println(2);
System.out.println(3);

これはスカラー置換です

ロックの除去

ロック除去とは、このロックはまったく役に立たないということです。jvmがエスケープ分析を実行しているときは、ロック除去を実行します。たとえば
、次のコードを確認します。

synchronized (new Object()){
    
    
    System.out.println(T0806_1.x);
    System.out.println(T0806_1.y);
}

実際、この方法では、同期はまったく効果がなく、役に立たないロックです。したがって、コードが実行されるとロックが解除されます。つまり、同期されたコードはまったくなく、実際には次のようになります。

System.out.println(T0806_1.x);
 System.out.println(T0806_1.y);

これはロックの除去です。JVMはエスケープ分析中にロックを除去します。

スタックへの割り当て

エスケープ分析が有効になっている場合、スタックに割り当てが存在します。
コードテストに合格

对象在堆区分配
对象在虚拟机栈上分配

-XX:+/-DoEscapeAnalysis(这个参数就是开启栈上分配)

空の場合は、下位スタックで割り当てをテストできます。10wオブジェクトを作成できます。HSDBツールを使用して、スタックで割り当てを開いたり閉じたりして、割り当てがあるかどうかを確認します。

おすすめ

転載: blog.csdn.net/scjava/article/details/108603633