これは、openjdk version "1.8.0_222"
(私の分析で使用した)OpenJDK 12.0.1
(Oleksandr Pyrohovによる)およびOpenJDK 13(Carlos Heubergerによる)で確実に再現できます(または、何をしたいかによっては再現できません)。
私は-XX:+PrintCompilation
両方の動作を取得するのに十分な時間をかけてコードを実行しましたが、ここに違いがあります。
バギー実装(出力を表示):
--- Previous lines are identical in both
54 17 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
54 23 3 LoopOutPut::test (57 bytes)
54 18 3 java.lang.String::<init> (82 bytes)
55 21 3 java.lang.AbstractStringBuilder::append (62 bytes)
55 26 4 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
55 20 3 java.lang.StringBuilder::<init> (7 bytes)
56 19 3 java.lang.StringBuilder::toString (17 bytes)
56 25 3 java.lang.Integer::getChars (131 bytes)
56 22 3 java.lang.StringBuilder::append (8 bytes)
56 27 4 java.lang.String::equals (81 bytes)
56 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) made not entrant
56 28 4 java.lang.AbstractStringBuilder::append (50 bytes)
56 29 4 java.lang.String::getChars (62 bytes)
56 24 3 java.lang.Integer::stringSize (21 bytes)
58 14 3 java.lang.String::getChars (62 bytes) made not entrant
58 33 4 LoopOutPut::test (57 bytes)
59 13 3 java.lang.AbstractStringBuilder::append (50 bytes) made not entrant
59 34 4 java.lang.Integer::getChars (131 bytes)
60 3 3 java.lang.String::equals (81 bytes) made not entrant
60 30 4 java.util.Arrays::copyOfRange (63 bytes)
61 25 3 java.lang.Integer::getChars (131 bytes) made not entrant
61 32 4 java.lang.String::<init> (82 bytes)
61 16 3 java.util.Arrays::copyOfRange (63 bytes) made not entrant
61 31 4 java.lang.AbstractStringBuilder::append (62 bytes)
61 23 3 LoopOutPut::test (57 bytes) made not entrant
61 33 4 LoopOutPut::test (57 bytes) made not entrant
62 35 3 LoopOutPut::test (57 bytes)
63 36 4 java.lang.StringBuilder::append (8 bytes)
63 18 3 java.lang.String::<init> (82 bytes) made not entrant
63 38 4 java.lang.StringBuilder::append (8 bytes)
64 21 3 java.lang.AbstractStringBuilder::append (62 bytes) made not entrant
正しい実行(表示なし):
--- Previous lines identical in both
55 23 3 LoopOutPut::test (57 bytes)
55 17 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
56 18 3 java.lang.String::<init> (82 bytes)
56 20 3 java.lang.StringBuilder::<init> (7 bytes)
56 21 3 java.lang.AbstractStringBuilder::append (62 bytes)
56 26 4 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
56 19 3 java.lang.StringBuilder::toString (17 bytes)
57 22 3 java.lang.StringBuilder::append (8 bytes)
57 24 3 java.lang.Integer::stringSize (21 bytes)
57 25 3 java.lang.Integer::getChars (131 bytes)
57 27 4 java.lang.String::equals (81 bytes)
57 28 4 java.lang.AbstractStringBuilder::append (50 bytes)
57 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) made not entrant
57 29 4 java.util.Arrays::copyOfRange (63 bytes)
60 16 3 java.util.Arrays::copyOfRange (63 bytes) made not entrant
60 13 3 java.lang.AbstractStringBuilder::append (50 bytes) made not entrant
60 33 4 LoopOutPut::test (57 bytes)
60 34 4 java.lang.Integer::getChars (131 bytes)
61 3 3 java.lang.String::equals (81 bytes) made not entrant
61 32 4 java.lang.String::<init> (82 bytes)
62 25 3 java.lang.Integer::getChars (131 bytes) made not entrant
62 30 4 java.lang.AbstractStringBuilder::append (62 bytes)
63 18 3 java.lang.String::<init> (82 bytes) made not entrant
63 31 4 java.lang.String::getChars (62 bytes)
大きな違いが1つあります。正しく実行すると、test()
2回コンパイルされます。最初に一度、その後もう一度(おそらくJITがメソッドがどれほどホットであるかを認識しているため)。バギーの実行でtest()
は5回コンパイル(または逆コンパイル)されます。
また、で実行されている-XX:-TieredCompilation
(その解釈、または使用のいずれかC2
)またはで-Xbatch
(代わりに平行のメインスレッドで実行するようにコンパイルを強制する)、出力がされて保証し、30000回の反復のプリントと多くのものから、そうC2
コンパイラは思わ犯人になる。これは-XX:TieredStopAtLevel=1
、を無効C2
にして出力を生成しないで実行することで確認されます(レベル4で停止すると再びバグが表示されます)。
正しい実行では、メソッドは最初にレベル3コンパイルでコンパイルされ、次にレベル4でコンパイルされます。
バギーな実行では、以前のコンパイルは破棄され(made non entrant
)、レベル3で再びコンパイルされます(つまりC1
、前のリンクを参照)。
したがって、それは間違いなくのバグですがC2
、レベル3のコンパイルに戻るという事実が影響するかどうかは確かではありません(なぜレベル3に戻るのか、まだ多くの不確実性があります)。
次の行でアセンブリコードを生成して、ウサギの穴のさらに深いところに移動できます(アセンブリの印刷を有効にするには、これも参照してください)。
java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm
この時点でスキルが不足し始めています。以前のコンパイル済みバージョンが破棄されると、バグのある動作が現れ始めますが、私が持っている小さな組み立てスキルは90年代のものなので、私よりも賢い人にそれを取らせますここから。
すべてのコードC2にはバグがないわけではないので、コードは他の誰かによってOPに提示されたため、これに関するバグレポートがすでにある可能性があります。この分析が他の人にとっても私にとって有益であったことを願っています。
由緒あるアパンギンがコメントで指摘したように、これは最近のバグです。すべての興味深く、役立つ人々に義務づけられています:)