Ciągle słyszę o wszystkich nowych fajnych funkcjach, które są dodawane do JVM, a jedną z tych fajnych funkcji jest wywołanie dynamiki. Chciałbym wiedzieć, co to jest i jak sprawia, że refleksyjne programowanie w Javie jest łatwiejsze lub lepsze?
java
reflection
jvm
invokedynamic
David K.
źródło
źródło
meth.invoke(args)
. Więc jak toinvokedynamic
pasujemeth.invoke
?MethodHandle
, o którym wspominam, jest mowa o tym samym rodzaju, ale z dużo większą elastycznością. Ale prawdziwa siła tkwi w tym wszystkim nie w dodatkach do języka Java, ale w możliwościach samej maszyny JVM we wspieraniu innych języków, które są z natury bardziej dynamiczne.invokedynamic
co czyni ją wydajną (w porównaniu z opakowaniem ich w anonimową klasę wewnętrzną, która była prawie jedynym wyborem przed wprowadzenieminvokedynamic
). Najprawdopodobniej wiele funkcjonalnych języków programowania korzystających z JVM zdecyduje się na kompilację do tego zamiast do anon-internal-classes.Jakiś czas temu C # dodał fajną funkcję, dynamiczną składnię w C #
Potraktuj to jako lukę składniową dla wywołań metod refleksyjnych. Może mieć bardzo ciekawe zastosowania. zobacz http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter, który jest odpowiedzialny za dynamiczny typ C #, właśnie przeszedł z SUN do MS. Nie jest więc nierozsądne myślenie, że te same rzeczy zostały omówione w SUN.
Pamiętam, że wkrótce potem jakiś koleś z Javy ogłosił coś podobnego
Niestety, tej funkcji nie ma w Javie 7. Bardzo rozczarowany. Programiści Java nie mają łatwego sposobu wykorzystania
invokedynamic
ich w swoich programach.źródło
invokedynamic
nigdy nie był przeznaczony do użytku przez programistów Java. IMO w ogóle nie pasuje do filozofii Java. Został dodany jako funkcja JVM dla języków innych niż Java.Przed kontynuacją przywoływania dynamiki należy zrozumieć dwie koncepcje.
1. Typowanie statyczne a Dynamin
Statyczny - sprawdzanie typu preform w czasie kompilacji (np. Java)
Dynamiczne - sprawdzanie typu preform w czasie wykonywania (np. JavaScript)
Sprawdzanie typu to proces sprawdzania, czy program jest bezpieczny, czyli sprawdzanie wpisanych informacji pod kątem zmiennych klas i instancji, parametrów metod, wartości zwracanych i innych zmiennych. Np. Java wie o int, String, .. w czasie kompilacji, podczas gdy typ obiektu w JavaScript można określić tylko w czasie wykonywania
2. Silne i słabe pisanie
Strong - określa ograniczenia dotyczące typów wartości dostarczanych do jego operacji (np. Java)
Weak - konwertuje (rzuca) argumenty operacji, jeśli te argumenty mają niekompatybilne typy (np. Visual Basic)
Wiedząc, że Java jest typem statycznym i słabo typowanym, jak zaimplementować języki dynamicznie i silnie typowane w JVM?
Invokedynamic implementuje system wykonawczy, który może wybrać najbardziej odpowiednią implementację metody lub funkcji - po skompilowaniu programu.
Przykład: mając (a + b) i nie wiedząc nic o zmiennych a, b w czasie kompilacji, invokedynamic mapuje tę operację na najbardziej odpowiednią metodę w Javie w czasie wykonywania. Na przykład, jeśli okaże się, że a, b są ciągami znaków, wywołaj metodę (String a, String b). Jeśli okaże się, że a, b są ints, wywołaj metodę (int a, int b).
invokedynamic został wprowadzony w Javie 7.
źródło
W ramach mojego artykułu o Java Records wypowiedziałem się na temat motywacji stojącej za Inoke Dynamic. Zacznijmy od przybliżonej definicji Indy.
Przedstawiamy Indy
Invoke Dynamic (znany również jako Indy ) był częścią JSR 292, którego celem było ulepszenie obsługi JVM dla dynamicznych języków typu. Po pierwszym wydaniu w Javie 7
invokedynamic
kod operacji wraz z jegojava.lang.invoke
bagażem jest używany dość intensywnie przez dynamiczne języki oparte na JVM, takie jak JRuby.Chociaż indy został specjalnie zaprojektowany, aby zwiększyć obsługę dynamicznego języka, oferuje znacznie więcej. W rzeczywistości nadaje się do użycia wszędzie tam, gdzie projektant języka potrzebuje jakiejkolwiek formy dynamiki, od dynamicznej akrobatyki po dynamiczne strategie!
Na przykład, wyrażenia Lambda Java 8 są faktycznie implementowane przy użyciu
invokedynamic
, mimo że Java jest językiem z typowaniem statycznym!Kod bajtowy definiowany przez użytkownika
Przez jakiś czas JVM obsługiwała cztery typy wywołań metod:
invokestatic
wywoływanie metod statycznych,invokeinterface
wywoływanie metod interfejsu,invokespecial
wywoływanie konstruktorówsuper()
lub metody prywatne iinvokevirtual
wywoływanie metod instancji.Pomimo różnic, te typy inwokacji mają jedną wspólną cechę: nie możemy ich wzbogacić naszą własną logiką . Wręcz przeciwnie,
invokedynamic
umożliwia nam załadowanie procesu wywołania w dowolny sposób. Następnie JVM dba o bezpośrednie wywołanie metody Bootstrapped.Jak działa Indy?
Gdy maszyna JVM po raz pierwszy widzi
invokedynamic
instrukcję, wywołuje specjalną metodę statyczną o nazwie Metoda Bootstrap . Metoda bootstrap to fragment kodu Java, który napisaliśmy, aby przygotować rzeczywistą logikę do wywołania:Następnie metoda bootstrap zwraca wystąpienie
java.lang.invoke.CallSite
. Odnosi sięCallSite
to do faktycznej metody, tjMethodHandle
.Odtąd za każdym razem, gdy JVM
invokedynamic
ponownie zobaczy tę instrukcję, pomija powolną ścieżkę i bezpośrednio wywołuje podstawowy plik wykonywalny. JVM nadal omija powolną ścieżkę, chyba że coś się zmieni.Przykład: Java 14 Records
Java 14
Records
zapewnia ładną, zwartą składnię do deklarowania klas, które mają być głupimi posiadaczami danych.Biorąc pod uwagę ten prosty rekord:
Kod bajtowy dla tego przykładu wyglądałby tak:
W tabeli metod Bootstrap :
Dlatego wywoływana jest metoda ładowania dla rekordów,
bootstrap
która znajduje się wjava.lang.runtime.ObjectMethods
klasie. Jak widać, ta metoda ładowania początkowego wymaga następujących parametrów:MethodHandles.Lookup
reprezentujące kontekst wyszukiwania (Ljava/lang/invoke/MethodHandles$Lookup
część).toString
,equals
,hashCode
, itd.) Bootstrap będzie link. Na przykład, gdy wartością jesttoString
, funkcja bootstrap zwróciConstantCallSite
(a,CallSite
które nigdy się nie zmienia), które wskazuje na rzeczywistątoString
implementację tego konkretnego rekordu.TypeDescriptor
Dla sposobu (Ljava/lang/invoke/TypeDescriptor
część).Class<?>
Reprezentujący typ klasy Record. Tak jestClass<Range>
w tym przypadku.min;max
.MethodHandle
na składnik. W ten sposób metoda bootstrap może utworzyć naMethodHandle
podstawie komponentów tej konkretnej implementacji metody.invokedynamic
Instrukcja przechodzi wszystkie te argumenty metody bootstrap. Z kolei metoda Bootstrap zwraca instancjęConstantCallSite
. TenConstantCallSite
trzyma odniesienie do żądanej metody realizacji, nptoString
.Dlaczego Indy?
W przeciwieństwie do interfejsów API Reflection, interfejs
java.lang.invoke
API jest dość wydajny, ponieważ maszyna JVM może w pełni przejrzeć wszystkie wywołania. Dlatego JVM może stosować wszelkiego rodzaju optymalizacje, o ile unikamy powolnej ścieżki tak długo, jak to możliwe!Oprócz argumentu dotyczącego wydajności,
invokedynamic
podejście to jest bardziej niezawodne i mniej kruche ze względu na swoją prostotę .Ponadto generowany kod bajtowy dla Java Records jest niezależny od liczby właściwości. Tak więc mniej kodu bajtowego i krótszy czas uruchamiania.
Na koniec załóżmy, że nowa wersja Java zawiera nową i bardziej wydajną implementację metody ładowania początkowego. Dzięki
invokedynamic
naszej aplikacji możemy skorzystać z tego ulepszenia bez ponownej kompilacji. W ten sposób mamy pewnego rodzaju kompatybilność binarną do przodu . Poza tym to dynamiczna strategia, o której mówiliśmy!Inne przykłady
Oprócz Java Records, dynamika wywołania została wykorzystana do implementacji funkcji takich jak:
LambdaMetafactory
StringConcatFactory
źródło