Ini dapat direproduksi secara andal (atau tidak direproduksi, tergantung pada apa yang Anda inginkan) dengan openjdk version "1.8.0_222"
(digunakan dalam analisis saya), OpenJDK 12.0.1
(menurut Oleksandr Pyrohov) dan OpenJDK 13 (menurut Carlos Heuberger).
Saya menjalankan kode dengan -XX:+PrintCompilation
waktu yang cukup untuk mendapatkan perilaku dan inilah perbedaannya.
Implementasi Buggy (menampilkan output):
--- 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
Proses yang benar (tidak ada tampilan):
--- 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)
Kita dapat melihat satu perbedaan signifikan. Dengan eksekusi yang benar, kami mengkompilasi test()
dua kali. Sekali di awal, dan sekali lagi sesudahnya (mungkin karena JIT memperhatikan betapa panas metode ini). Dalam eksekusi buggy test()
dikompilasi (atau didekompilasi) 5 kali.
Selain itu, berjalan dengan -XX:-TieredCompilation
(yang mengartikan, atau menggunakan C2
) atau dengan -Xbatch
(yang memaksa kompilasi untuk berjalan di utas utama, bukannya paralel), output dijamin dan dengan 30000 iterasi mencetak banyak hal, sehingga C2
kompiler tampaknya untuk menjadi pelakunya. Ini dikonfirmasi dengan berjalan dengan -XX:TieredStopAtLevel=1
, yang menonaktifkan C2
dan tidak menghasilkan output (berhenti di level 4 menunjukkan bug lagi).
Dalam eksekusi yang benar, metode ini pertama kali dikompilasi dengan kompilasi Level 3 , kemudian setelah itu dengan Level 4.
Dalam eksekusi buggy, kompilasi sebelumnya diabaikan ( made non entrant
) dan dikompilasi lagi pada Level 3 (yaitu C1
, lihat tautan sebelumnya).
Jadi itu pasti bug C2
, walaupun saya tidak benar-benar yakin apakah fakta bahwa itu akan kembali ke kompilasi Level 3 memengaruhinya (dan mengapa itu kembali ke level 3, masih banyak ketidakpastian masih).
Anda dapat membuat kode rakitan dengan baris berikut untuk masuk lebih dalam ke lubang kelinci (lihat juga ini untuk mengaktifkan pencetakan rakitan).
java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm
Pada titik ini saya mulai kehabisan keterampilan, perilaku buggy mulai menunjukkan ketika versi kompilasi sebelumnya dibuang, tetapi betapa sedikit keterampilan perakitan yang saya miliki dari tahun 90-an, jadi saya akan membiarkan seseorang yang lebih pintar daripada saya mengambilnya dari sini.
Kemungkinan sudah ada laporan bug tentang ini, karena kode tersebut disampaikan kepada OP oleh orang lain, dan karena semua kode C2 bukan tanpa bug . Saya berharap analisis ini bermanfaat bagi orang lain seperti juga bagi saya.
Seperti yang ditunjukkan oleh Yang Mulia Apangin dalam komentar, ini adalah bug baru - baru ini . Banyak yang diwajibkan untuk semua orang yang tertarik dan membantu :)