Zacznijmy od dwóch prostych klas:
package com.michaelt.so.supers;
public class Sup {
int methodA(int a, int b) {
return a + b;
}
}
i wtedy
package com.michaelt.so.supers;
public class Sub extends Sup {
@Override
int methodA(int a, int b) {
return super.methodA(a, b);
}
}
Metoda kompilacji A i patrząc na kod bajtowy otrzymujemy:
methodA(II)I
L0
LINENUMBER 6 L0
ALOAD 0
ILOAD 1
ILOAD 2
INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
IRETURN
L1
LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
LOCALVARIABLE a I L0 L1 1
LOCALVARIABLE b I L0 L1 2
MAXSTACK = 3
MAXLOCALS = 3
I możesz zobaczyć w tym miejscu za pomocą metody invokespecial, która wyszukuje metodę klasy SupAA ().
Invokespecial opcode ma następującą logiką:
- Jeśli C zawiera deklarację dla metody instancji o tej samej nazwie i deskryptorze co metoda rozstrzygnięta, wówczas ta metoda zostanie wywołana. Procedura wyszukiwania kończy się.
- W przeciwnym razie, jeśli C ma nadklasę, ta sama procedura wyszukiwania jest wykonywana rekurencyjnie przy użyciu bezpośredniej nadklasy C. Metoda, która ma zostać wywołana, jest wynikiem rekurencyjnego wywołania tej procedury wyszukiwania.
- W przeciwnym razie wywoływany jest błąd AbstractMethodError.
W tym przypadku nie ma metody instancji o tej samej nazwie i deskryptorze w swojej klasie, więc pierwsza kula nie będzie strzelać. Druga kula jednak będzie - istnieje nadklasa, która wywołuje metodę superA.
Kompilator nie uwzględnia tego i nie ma kopii źródła Sup w klasie.
Jednak historia nie jest jeszcze skończona. To tylko skompilowany kod. Gdy kod trafi do JVM, HotSpot może się zaangażować.
Niestety nie wiem zbyt wiele na ten temat, więc odwołam się do autorytetu w tej sprawie i przejdę do Inlining w Javie, gdzie mówi się, że HotSpot może wstawiać metody (nawet metody nie-końcowe).
Przechodząc do dokumentacji , należy zauważyć, że jeśli określone wywołanie metody staje się gorącym punktem zamiast wykonywania tego wyszukiwania za każdym razem, informacje te można wstawić - skutecznie kopiując kod z metody Sup A () do metody Sub (A).
Odbywa się to w czasie wykonywania, w pamięci, w oparciu o zachowanie aplikacji i jakie optymalizacje są konieczne, aby przyspieszyć działanie.
Jak stwierdzono w HotSpot Internals dla OpenJDK „Metody są często wprowadzane. Wywołania statyczne, prywatne, końcowe i / lub„ specjalne ”są łatwe do wprowadzenia”.
Jeśli zagłębisz się w opcje JVM , znajdziesz opcję -XX:MaxInlineSize=35
(domyślnie 35), która jest maksymalną liczbą bajtów, którą można wstawić. Zaznaczę, że właśnie dlatego Java lubi mieć wiele małych metod - ponieważ można je łatwo wprowadzić. Te małe metody stają się szybsze, gdy są nazywane więcej, ponieważ można je wstawiać. I chociaż można grać z tą liczbą i powiększać ją, może to powodować, że inne optymalizacje będą mniej skuteczne. (powiązane pytanie SO: strategia wstawiania HotSpot JIT, która wskazuje na szereg innych opcji, aby zajrzeć do wewnętrznych elementów wstawiania, które robi HotSpot).
Zatem nie - kod nie jest wstawiany podczas kompilacji. I tak - kod można bardzo dobrze wprowadzić w czasie wykonywania, jeśli wymagają tego optymalizacje wydajności.
A wszystko, co napisałem o wstawianiu HotSpot dotyczy tylko HotSpot JVM dystrybuowanego przez Oracle. Jeśli spojrzysz na listę wirtualnych maszyn Java w Wikipedii, jest o wiele więcej niż HotSpot, a sposób, w jaki te maszyny JVM obsługują wstawianie, może być zupełnie inny niż to, co opisałem powyżej. Apache Harmony, Dalvik, ART - tam rzeczy mogą działać inaczej.