JEP 280:Indify String Concatenation で書かれているように:
String
によって生成される静的javac
- concatenationバイトコードシーケンスを変更して、JDKライブラリ関数へのinvokedynamic
呼び出しを使用します。これにより、String
によって送信されるバイトコードをさらに変更することなく、javac
連結の将来の最適化が可能になります。
ここで、invokedynamic
呼び出しの使用法と、バイトコード連結がinvokedynamic
とどのように異なるかを理解したいと思います。
「古い」方法は、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メソッドとして使用されます。
私の意見では、文字列連結の最適化に使用されるinvokedynamic
実装の詳細に入る前に、 invokedynamicとは何ですか?
invokedynamic
命令は単純化され、潜在的には、JVM上の動的言語のコンパイラーとランタイムシステムの実装を改善します。これは、言語実装者が以下のステップを含むinvokedynamic
命令でカスタムリンケージの動作を定義できるようにすることで実現します。
String連結最適化の実装のためにもたらされた変更を使用して、これらの方法を説明します。
Bootstrapメソッドの定義:-:Java9では、invokedynamic
呼び出しサイトのbootstrapメソッド、文字列の連結をサポート主に makeConcat
および makeConcatWithConstants
はStringConcatFactory
実装で導入されました。
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でのアプリケーションのベンチマークの例を示していますさまざまな実装間で見える。
ここで少し詳細を追加します。取得する主な部分は、文字列の連結がどのように行われるかということですランタイムの決定ではなく、コンパイル時の決定です。したがって、コードをコンパイルしたことを意味します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
はまだオプションであることに注意してください。