Myślę, że większość z was wie, że goto
jest to zastrzeżone słowo kluczowe w języku Java, ale tak naprawdę nie jest używane. Prawdopodobnie wiesz również, że goto
jest to kod operacyjny Java Virtual Machine (JVM). Sądzę wszystkich skomplikowanych struktur przepływu kontrola Java, Scala i Kotlin są na poziomie JVM, realizowane przy użyciu niektórych kombinacji goto
i ifeq
, ifle
, iflt
, itd.
Patrząc na specyfikację JVM https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w Widzę, że istnieje również goto_w
kod operacyjny. Podczas gdy goto
przyjmuje 2-bajtowe przesunięcie gałęzi, goto_w
przyjmuje 4-bajtowe przesunięcie gałęzi. Specyfikacja mówi, że
Chociaż instrukcja goto_w przyjmuje 4-bajtowe przesunięcie gałęzi, inne czynniki ograniczają rozmiar metody do 65535 bajtów (§4.11). Limit ten może zostać podniesiony w przyszłej wersji wirtualnej maszyny Java.
To brzmi dla mnie jak goto_w
jest przyszłość pokryć, jak niektóre inne *_w
rozkazy. Ale zdaje mi się, że możegoto_w
może można by go użyć z dwoma bardziej znaczącymi bajtami wyzerowanymi i dwoma mniej znaczącymi bajtami takimi samymi jak dla goto
, z koniecznymi zmianami.
Na przykład, biorąc pod uwagę ten Java Switch-Case (lub 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.
moglibyśmy przepisać to jako
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.
Tak naprawdę nie próbowałem tego, ponieważ prawdopodobnie popełniłem błąd, zmieniając „numery linii”, aby uwzględnić goto_w
s. Ale ponieważ jest to w specyfikacji, powinno być to możliwe.
Moje pytanie brzmi, czy istnieje powód, dla którego kompilator lub inny generator kodu bajtowego mógłby użyć goto_w
przy obecnym limicie 65535, aby pokazać, że można to zrobić?
// ... repeat 10K times ...
To się kompiluje? Wiem, że istnieje limit wielkości pojedynczej klasy źródłowej ... ale nie wiem, co to dokładnie jest (generowanie kodu to jedyny raz, kiedy widziałem, że coś w to uderzyło).Nie ma powodu, aby używać,
goto_w
gdy gałąź pasuje dogoto
. Ale wydaje się, że przegapiłeś, że gałęzie są względne , używając przesunięcia z podpisem, ponieważ gałąź może również cofnąć się.Nie zauważysz tego, patrząc na wynik takiego narzędzia
javap
, ponieważ oblicza wynikowy bezwzględny adres docelowy przed drukowaniem.Tak więc
goto
zasięg-327678 … +32767
nie zawsze wystarcza, aby zająć się każdą możliwą lokalizacją docelową w0 … +65535
zasięgu.Na przykład następująca metoda będzie miała
goto_w
instrukcję na początku:Demo na Ideone
źródło
Main
zmethodWithLargeJump()
gromadzi do niemal 400KB.finally
bloki są duplikowane dla normalnego i wyjątkowego przepływu (obowiązkowe od Java 6). Zatem zagnieżdżenie dziesięciu z nich oznacza × 2¹⁰, wtedy przełącznik zawsze ma domyślny cel, więc wraz z iloadem potrzebuje dziesięciu bajtów plus dopełniania. Dodałem także nietrywialną instrukcję w każdej gałęzi, aby zapobiec optymalizacji. Wykorzystywanie limitów to powracający temat, zagnieżdżone wyrażenia , lambdy , pola , konstruktory …Wygląda na to, że w niektórych kompilatorach (wypróbowanych w 1.6.0 i 11.0.7), jeśli metoda jest wystarczająco duża i zawsze potrzebuje goto_w, używa wyłącznie goto_w. Nawet jeśli ma bardzo lokalne skoki, nadal używa goto_w.
źródło