web-dev-qa-db-ja.com

Java 9で文字列連結はどのように実装されますか?

JEP 280:Indify String Concatenation で書かれているように:

Stringによって生成される静的javac- concatenationバイトコードシーケンスを変更して、JDKライブラリ関数へのinvokedynamic呼び出しを使用します。これにより、Stringによって送信されるバイトコードをさらに変更することなく、javac連結の将来の最適化が可能になります。

ここで、invokedynamic呼び出しの使用法と、バイトコード連結がinvokedynamicとどのように異なるかを理解したいと思います。

107
Mohit Tyagi

「古い」方法は、StringBuilder指向の操作の束を出力します。このプログラムを検討してください:

public class Example {
    public static void main(String[] args)
    {
        String result = args[0] + "-" + args[1] + "-" + args[2];
        System.out.println(result);
    }
}

JDK 8以前でコンパイルし、javap -c Exampleを使用してバイトコードを表示すると、次のように表示されます。

 public class Example {
 public Example(); 
 Code:
 0:aload_0 
 1:invokespecial#1 //メソッドJava/lang /Object."<init>":()V
 4:return 
 
 public static void main(Java.lang.String []); 
 Code :
 0:新しい#2 //クラスJava/lang/StringBuilder 
 3:dup 
 4:invokespecial#3 //メソッドJava/lang/StringBuilder。 "<init> ":()V 
 7:aload_0 
 8:iconst_0 
 9:aaload 
 10:invokevirtual#4 //メソッドJava/lang/StringBuilder.append: (Ljava/lang/String;)Ljava/lang/StringBuilder; 
 13:ldc#5 //文字列-
 15:invokevirtual#4 //メソッドJava/lang/StringBuilder.append :( Ljava/lang/String;)Ljava/lang/StringBuilder; 
 18:aload_0 
 19:iconst_1 
 20:aaload 
 2 1:invokevirtual#4 //メソッドJava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
 24:ldc#5 //文字列-
 26 :invokevirtual#4 //メソッドJava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
 29:aload_0 
 30:iconst_2 
 31:aaload 
 32:invokevirtual#4 //メソッドJava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
 35:invokevirtual#6 //メソッドJava/lang/StringBuilder.toString :()Ljava/lang/String; 
 38:astore_1 
 39:getstatic#7 //フィールドJava/lang/System.out:Ljava/io/PrintStream; 
 42:aload_1 
 43:invokevirtual#8 //メソッドJava/io/PrintStream.println:(Ljava/lang/String;)V 
 46:return 
} 

ご覧のとおり、StringBuilderを作成し、appendを使用します。 StringBuilderの組み込みバッファのデフォルトの容量は16文字しかないため、これはかなり非効率的であることが有名です。compilerが事前にさらに割り当てることを知る方法がないため、終了します再割り当てする必要があります。また、メソッド呼び出しの束でもあります。 (ただし、JVMはこれらの呼び出しパターンを時々検出および書き換えて、より効率的にすることができます。)

Java 9が生成するものを見てみましょう。

 public class Example {
 public Example(); 
 Code:
 0:aload_0 
 1:invokespecial#1 //メソッドJava/lang /Object."<init>":()V
 4:return 
 
 public static void main(Java.lang.String []); 
 Code :
 0:aload_0 
 1:iconst_0 
 2:aaload 
 3:aload_0 
 4:iconst_1 
 5:aaload 
 6:aload_0 
 7:iconst_2 
 8:aaload 
 9:invokedynamic#2、0 // InvokeDynamic#0:makeConcatWithConstants:(Ljava/lang/String; Ljava/lang/String; Ljava/lang/String;)Ljava/lang/String; 
 14:astore_1 
 15:getstatic#3 //フィールドJava/lang/System.out:Ljava/io/PrintStream; 
 18:aload_1 
 19:invokevirtual#4 //メソッドJava/io/PrintStream.println:(Ljava/lang/String;)V 
 22:return 
} 

ああ、それはもっと短い。 :-)それは makeConcatWithConstants からStringConcatFactoryを1回呼び出します。

文字列連結メソッドの作成を容易にするメソッド。これは、おそらく型適応および引数の部分評価の後、既知の型の既知の数の引数を効率的に連結するために使用できます。これらのメソッドは、通常、Javaプログラミング言語のstring concatenation機能をサポートするために、invokedynamic呼び出しサイトのbootstrapメソッドとして使用されます。

93
T.J. Crowder

私の意見では、文字列連結の最適化に使用されるinvokedynamic実装の詳細に入る前に、 invokedynamicとは何ですか?

invokedynamic命令は単純化され、潜在的には、JVM上の動的言語のコンパイラーとランタイムシステムの実装を改善します。これは、言語実装者が以下のステップを含むinvokedynamic命令でカスタムリンケージの動作を定義できるようにすることで実現します。


String連結最適化の実装のためにもたらされた変更を使用して、これらの方法を説明します。

  • Bootstrapメソッドの定義:-:Java9では、invokedynamic呼び出しサイトのbootstrapメソッド、文字列の連結をサポート主に makeConcat および makeConcatWithConstantsStringConcatFactory実装で導入されました。

    Invokedynamicを使用すると、実行時まで翻訳戦略を選択する代替手段が提供されます。 StringConcatFactoryで使用される変換戦略は、以前のJavaバージョンで導入されたLambdaMetafactoryに似ています。さらに、質問で言及されたJEPの目標の1つは、これらの戦略をさらに拡大することです。

  • 定数プールエントリの指定:-これらは、(1)以外のinvokedynamic命令への追加の静的引数です。 MethodHandles.Lookup のコンテキストでメソッドハンドルを作成するためのファクトリであるオブジェクトinvokedynamic命令、(2)Stringオブジェクト、動的呼び出しサイトで言及されているメソッド名、および(3) MethodType オブジェクト、動的呼び出しサイトの解決された型シグネチャ。

    コードのリンク中に既にリンクされています。実行時に、bootstrapメソッドが実行され、連結を行う実際のコードにリンクします。適切なinvokedynamic呼び出しでinvokestatic呼び出しを書き換えます。これは、定数プールから定数文字列をロードし、bootstrapメソッドの静的引数を利用して、これらの定数およびその他の定数をbootstrapメソッド呼び出しに直接渡します。 .

  • invokedynamic Instructionの使用:-これは、最初の呼び出し中に呼び出しターゲットをbootstrapする手段を提供することにより、遅延リンクの機能を提供します。ここでの最適化の具体的なアイデアは、StringBuilder.appendダンス全体をJava.lang.invoke.StringConcatFactoryの単純なinvokedynamic呼び出しに置き換えることです。これは、連結が必要な値を受け入れます。

Indify String Concatenation 提案では、 @ TJ Crowder によって共有されているのと同様のメソッドがコンパイルされ、バイトコードの違いがかなりあるJava9でのアプリケーションのベンチマークの例を示していますさまざまな実装間で見える。

20
Naman

ここで少し詳細を追加します。取得する主な部分は、文字列の連結がどのように行われるかということですランタイムの決定ではなく、コンパイル時の決定です。したがって、コードをコンパイルしたことを意味しますJava-9に対して1回であり、再コンパイルすることなく基本的な実装を変更できます。

2番目のポイントは、現時点では6 possible strategies for concatenation of Stringがあることです。

 private enum Strategy {
    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder}.
     */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that in the end calls into {@link Java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link Java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
}

パラメータ-Djava.lang.invoke.stringConcatを介してそれらのいずれかを選択できます。 StringBuilderはまだオプションであることに注意してください。

18
Eugene