Saya pikir sebagian besar dari Anda tahu bahwa goto
kata kunci yang disediakan dalam bahasa Jawa tetapi tidak benar-benar digunakan. Dan Anda mungkin juga tahu itu goto
adalah opcode Java Virtual Machine (JVM). Saya rasa semua struktur aliran kontrol canggih dari Jawa, Scala dan Kotlin yang, pada tingkat JVM, dilaksanakan dengan menggunakan beberapa kombinasi dari goto
dan ifeq
, ifle
, iflt
, dll
Melihat spesifikasi JVM https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w Saya melihat ada juga goto_w
opcode. Sedangkan goto
mengambil cabang offset 2-byte, goto_w
mengambil cabang offset 4-byte. Spesifikasi menyatakan itu
Meskipun instruksi goto_w mengambil offset cabang 4-byte, faktor lain membatasi ukuran metode hingga 65535 byte (§4.11). Batas ini dapat dinaikkan pada rilis Java Virtual Machine yang akan datang.
Bagi saya kedengarannya seperti goto_w
pembuktian di masa depan, seperti beberapa *_w
opcode lainnya . Tetapi itu juga terjadi pada saya bahwa mungkin goto_w
dapat digunakan dengan dua byte lebih signifikan memusatkan perhatian dan dua byte kurang signifikan sama seperti untuk goto
, dengan penyesuaian yang diperlukan.
Sebagai contoh, dengan diberikan Java Switch-Case (atau Scala Match-Case):
12: lookupswitch {
112785: 48 // case "red"
3027034: 76 // case "green"
98619139: 62 // case "blue"
default: 87
}
48: aload_2
49: ldc #17 // String red
51: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
54: ifeq 87
57: iconst_0
58: istore_3
59: goto 87
62: aload_2
63: ldc #19 // String green
65: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
68: ifeq 87
71: iconst_1
72: istore_3
73: goto 87
76: aload_2
77: ldc #20 // String blue
79: invokevirtual #18
// etc.
kita bisa menulis ulang sebagai
12: lookupswitch {
112785: 48
3027034: 78
98619139: 64
default: 91
}
48: aload_2
49: ldc #17 // String red
51: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
54: ifeq 91 // 00 5B
57: iconst_0
58: istore_3
59: goto_w 91 // 00 00 00 5B
64: aload_2
65: ldc #19 // String green
67: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
70: ifeq 91
73: iconst_1
74: istore_3
75: goto_w 91
79: aload_2
81: ldc #20 // String blue
83: invokevirtual #18
// etc.
Saya belum benar-benar mencoba ini, karena saya mungkin telah membuat kesalahan dengan mengubah "nomor baris" untuk mengakomodasi goto_w
. Tetapi karena itu ada dalam spesifikasi, itu harus dimungkinkan untuk melakukannya.
Pertanyaan saya adalah apakah ada alasan kompiler atau generator bytecode lainnya dapat digunakan goto_w
dengan batas 65535 saat ini selain untuk menunjukkan bahwa hal itu dapat dilakukan?
// ... repeat 10K times ...
Kompilasi itu? Saya tahu ada batasan untuk ukuran satu sumber kelas ... tapi saya tidak tahu persis apa itu (pembuatan kode adalah satu-satunya waktu saya melihat sesuatu yang benar-benar memukulnya).Tidak ada alasan untuk digunakan
goto_w
saat cabang cocok dengan agoto
. Tetapi Anda tampaknya telah melewatkan bahwa cabang adalah relatif , menggunakan offset yang ditandatangani, karena cabang juga dapat mundur.Anda tidak melihatnya ketika melihat output dari alat seperti
javap
, karena menghitung alamat target absolut yang dihasilkan sebelum mencetak.Jadi
goto
jangkauan-327678 … +32767
tidak selalu cukup untuk mengatasi setiap kemungkinan lokasi target dalam0 … +65535
jangkauan.Misalnya, metode berikut akan memiliki
goto_w
instruksi di awal:Demo di Ideone
sumber
Main
denganmethodWithLargeJump()
kompilasi hingga hampir 400KB.finally
blok diduplikasi untuk aliran normal dan luar biasa (wajib sejak Java 6). Jadi sepuluh dari mereka menyiratkan × 2¹⁰, maka, switch selalu memiliki target default, jadi bersama dengan iload, perlu sepuluh byte plus padding. Saya juga menambahkan pernyataan nontrivial di setiap cabang untuk mencegah optimisasi. Batas pemanfaatan adalah topik berulang, ekspresi bersarang , lambda , bidang , konstruktor ...Tampaknya dalam beberapa kompiler (dicoba pada 1.6.0 dan 11.0.7), jika suatu metode cukup besar yang pernah membutuhkan goto_w, ia menggunakan secara eksklusif goto_w. Bahkan ketika ia memiliki lompatan yang sangat lokal, ia masih menggunakan goto_w.
sumber