jdk-9文字列連結にどれくらいの労力を費やしたかは驚くでしょう。最初のjavacはStringBuilder#append
への呼び出しの代わりにinvokedynamic
を発行します。 invokedynamicはCallSite
を返し、MethodHandle(実際は一連のMethodHandlesです)を含みます。
したがって、文字列連結に対して実際に何が行われたかは、ランタイムに移されます。欠点は、同じタイプの引数に対して、より遅くなるStringsを初めて連結することです。
は、その後、あなたが文字列を連結するときから選択することができます戦略のシリーズは、(あなたがjava.lang.invoke.stringConcat
パラメータでデフォルトのものをオーバーライドすることができます)があります。
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
}
デフォルトの戦略は次のとおりです。MH_INLINE_SIZED_EXACT
獣です!
は、それは文字列(最速)を構築するために、パッケージ・プライベートコンストラクタを使用しています。
/*
* Package private constructor which shares value array for speed.
*/
String(byte[] value, byte coder) {
this.value = value;
this.coder = coder;
}
まずこの戦略はそうフィルタはと呼ばれる作成されます。これらは基本的に、着信パラメータをString値に変換するメソッドハンドルです。
String.valueOf(YourInstance)
だから、あなたが委任します3 MethodHandlesがあるだろう連結する3つのオブジェクトを持っている場合:1が予想されるように、これらのMethodHandlesは、ほとんどの場合、呼び出しをMethodHandleを生成することStringifiers
と呼ばれるクラスに格納されていますString.valueOf(YourObject)
にすると、オブジェクトを文字列に変換したことを意味します。 このクラスの中にはまだ理解できないものがあります。別のクラスStringifierMost
(これはStringのみのリファレンス、floatとdoubleに変換されます)とStringifierAny
を持つ必要があります。
MH_INLINE_SIZED_EXACT
では、バイト配列は正確なサイズに計算されるため、それを計算する方法があります。
これは、入力パラメータ(References/float/double)の文字列化されたバージョンを取るStringConcatHelper#mixLen
のメソッドによって行われます。この時点で、私たちは最終的なStringのサイズを知っています。さて、我々は実際にそれを知らない、我々はそれを計算するMethodHandleがあります。
ここで言及する価値のある文字列jdk-9にもう1つ変更があります。coder
フィールドが追加されました。これは、Stringのsize/equality/charAtを計算するために必要です。サイズに必要なので、それも計算する必要があります。これはStringConcatHelper#mixCoder
によって行われます。
それはウル配列を作成しますMethodHandleを委任するために、この時点では安全である:
@ForceInline
private static byte[] newArray(int length, byte coder) {
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
}
は、どのように各要素が追加されますか?方法はStringConcatHelper#prepend
です。
今や、バイトを取るStringのコンストラクタを呼び出すために必要なすべての詳細が必要になります。
すべてのこれらの操作(および他の多くの私は簡単にするためにスキップされている)は、添付が実際に発生したときに呼び出されますMethodHandle発光を介して処理されています。
式を連結するときの再入可能性に注意してください。 – SLaks
最後に、それはかなり愚かで、 'StringBuilder'に繰り返し割り当てを強制しました。しかし、これはOracleのJDKに固有のもので、結果として生じるバイトコードを見ているため、JVMが行う可能性のある最適化を考慮していませんでした。私のルールは、あなたが気にしない時間の99.999%です。あなたが気にしている.001%のために、合計結果を処理するのに十分な大きさに割り当てられた明示的な 'StringBuilder'を使用してください。 –
1行だけではなく、文字列操作を多くしない限り、私はT.J.に同意します。何も違いは見られません。 JVMは、実際には、スレッドのローカルにすべてのメモリを割り当てます(別のスレッドと共有する必要があるまで)。つまり、スレッドのローカルにはおそらく何の効果もありません。 – markspace