Programování

Invokedynamic 101

Vydání Oracle Java 7 představilo nové invokedynamic instrukce bytecode do Java Virtual Machine (JVM) a nový java.lang.invoke Balíček API do standardní knihovny tříd. Tento příspěvek vás seznámí s touto instrukcí a API.

Co a jak invokedynamic

Otázka: Co je invokedynamic?

A:invokedynamic je instrukce bytecode, která usnadňuje implementaci dynamických jazyků (pro JVM) prostřednictvím vyvolání dynamické metody. Tato instrukce je popsána v Java SE 7 Edition specifikace JVM.

Dynamické a statické jazyky

A dynamický jazyk (také známý jako dynamicky psaný jazyk) je programovací jazyk na vysoké úrovni, jehož kontrola typu se obvykle provádí za běhu, což je funkce známá jako dynamické psaní. Kontrola typu ověří, že program je typ bezpečný: všechny argumenty operace mají správný typ. Groovy, Ruby a JavaScript jsou příklady dynamických jazyků. (The @ groovy.transform.TypeChecked anotace způsobí, že Groovy zadá kontrolu v době kompilace.)

Naproti tomu a statický jazyk (také známý jako staticky psaný jazyk) provádí kontrolu typu v době kompilace, což je funkce známá jako statické psaní. Kompilátor ověří, zda je program správný, i když může odložit nějakou kontrolu typu za běhu (myslím, že casts a checkcast návod). Java je příkladem statického jazyka. Kompilátor Java používá tyto informace o typu k vytvoření silně zadaného bajtkódu, který může JVM efektivně provádět.

Otázka: Jak invokedynamic usnadnit dynamickou implementaci jazyka?

A: V dynamickém jazyce ke kontrole typu obvykle dochází za běhu. Vývojáři musí předat vhodné typy nebo riskovat selhání za běhu. Často tomu tak je java.lang.Object je nejpřesnější typ argumentu metody. Tato situace komplikuje kontrolu typu, která ovlivňuje výkon.

Další výzvou je, že dynamické jazyky obvykle nabízejí možnost přidávat pole / metody do a odebrat je ze stávajících tříd. Ve výsledku je nutné odložit třídu, metodu a rozlišení pole za běhu. Často je také nutné přizpůsobit vyvolání metody cíli, který má jiný podpis.

Tyto výzvy tradičně vyžadují, aby byla na vrcholu JVM vybudována runtime podpora ad hoc. Tato podpora zahrnuje třídy typu obálky, použití hash tabulek k zajištění dynamického rozlišení symbolů atd. Bytecode je generován se vstupními body do modulu runtime ve formě volání metod pomocí kterékoli ze čtyř instrukcí vyvolání metody:

  • invokestatický se používá k vyvolání statický metody.
  • invokevirtual se používá k vyvolání veřejnost a chráněný ne-statický metody prostřednictvím dynamického odesílání.
  • vyvolatrozhraní je podobný invokevirtual kromě odeslání metody založené na typu rozhraní.
  • speciální se používá k vyvolání metod inicializace instance (konstruktorů) a také soukromé metody a metody nadtřídy současné třídy.

Tato runtime podpora ovlivňuje výkon. Generovaný bytecode často vyžaduje několik skutečných vyvolání metody JVM pro jedno vyvolání metody dynamického jazyka. Reflexe je široce používána a přispívá ke snížení výkonu. Mnoho různých cest provádění také znemožňuje, aby kompilátor JVM just-in-time (JIT) použil optimalizace.

Chcete-li řešit špatný výkon, invokedynamic instrukce odstraňuje runtime podporu ad hoc. Místo toho první hovor bootstrapy vyvoláním runtime logiky, která efektivně vybere cílovou metodu, a následná volání obvykle vyvolají cílovou metodu, aniž by bylo nutné znovu zavádět bootstrap.

invokedynamic přináší výhody také implementátorům dynamických jazyků podporou dynamicky se měnících cílů stránek volání - a volejte webkonkrétněji a stránka dynamického volání je invokedynamic návod. Dále proto, že JVM interně podporuje invokedynamic, tuto instrukci může lépe optimalizovat kompilátor JIT.

Metoda úchyty

Otázka: Rozumím tomu invokedynamic pracuje s popisovači metod pro usnadnění vyvolání dynamické metody. Co je popisovač metody?

A: A rukojeť metody je „typizovaný, přímo spustitelný odkaz na podkladovou metodu, konstruktor, pole nebo podobnou operaci na nízké úrovni s volitelnými transformacemi argumentů nebo návratových hodnot.“ Jinými slovy, je to podobné jako u funkčního ukazatele ve stylu C, který ukazuje na spustitelný kód - a cílová - a které lze dereferencovat k vyvolání tohoto kódu. Popisy metod jsou popsány v abstraktu java.lang.invoke.MethodHandle třída.

Otázka: Můžete uvést jednoduchý příklad vytvoření a vyvolání metody zpracování?

A: Podívejte se na výpis 1.

Výpis 1. MHD.java (verze 1)

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MHD {public static void main (String [] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findStatic (MHD.class, "ahoj", MethodType.methodType (void.class)); mh.invokeExact (); } static void hello () {System.out.println ("hello"); }}

Výpis 1 popisuje ukázkový program pro zpracování metody skládající se z hlavní() a Ahoj() třídní metody. Cílem tohoto programu je vyvolat Ahoj() pomocí popisovače metody.

hlavní()Prvním úkolem je získat a java.lang.invoke.MethodHandles.Lookup objekt. Tento objekt je továrna na vytváření popisovačů metod a používá se k hledání cílů, jako jsou virtuální metody, statické metody, speciální metody, konstruktory a přístupové pole. Dále je to závislé na kontextu vyvolání stránky volání a vynucuje omezení přístupu ke zpracovávání metod pokaždé, když je vytvořen popisovač metody. Jinými slovy, web pro volání (například seznam 1 hlavní() Metoda fungující jako web volání), který získá vyhledávací objekt, může přistupovat pouze k těm cílům, které jsou přístupné webu volání. Vyhledávací objekt je získán vyvoláním java.lang.invoke.MethodHandles třídy MethodHandles.Lookup lookup () metoda.

publicLookup ()

MethodHandles také deklaruje a MethodHandles.Lookup publicLookup () metoda. Na rozdíl od vzhlédnout(), který lze použít k získání popisovače metody pro jakoukoli přístupnou metodu / konstruktor nebo pole, publicLookup () lze použít k získání popisovače metody pouze k veřejně přístupnému poli nebo k veřejně přístupné metodě / konstruktoru.

Po získání vyhledávacího objektu je tento objekt MethodHandle findStatic (Class refc, String name, MethodType type) metoda se volá k získání popisovače metody do Ahoj() metoda. První argument předán findStatic () je odkaz na třídu (MHD) ze kterého je metoda (Ahoj()) je přístupný a druhým argumentem je název metody. Třetí argument je příkladem a typ metody, který „představuje argumenty a návratový typ přijatý a vrácený popisovačem metody, nebo argumenty a návratový typ předaný a očekávaný volajícím popisovačem metody.“ Představuje to instance java.lang.invoke.MethodType třídy a získané (v tomto příkladu) voláním java.lang.invoke.MethodTypeje MethodType methodType (třída rtype) metoda. Tato metoda se nazývá protože Ahoj() poskytuje pouze návratový typ, který se stane prázdnota. Tento návratový typ je k dispozici pro methodType () kolem neplatnost. třída k této metodě.

Vrácený popisovač metody je přiřazen mh. Tento objekt se poté používá k volání MethodHandleje Object invokeExact (Object ... args) method, k vyvolání popisovače metody. Jinými slovy, invokeExact () výsledky v Ahoj() být volán a Ahoj se zapisuje do standardního výstupního proudu. Protože invokeExact () je prohlášen za hod Vrhací, Připojil jsem se hodí Throwable do hlavní() záhlaví metody.

Otázka: Ve své předchozí odpovědi jste zmínili, že vyhledávací objekt může přistupovat pouze k těm cílům, které jsou přístupné webu volání. Můžete uvést příklad, který ukazuje, jak se pokusit získat popisovač metody k nepřístupnému cíli?

A: Podívejte se na výpis 2.

Výpis 2. MHD.java (verze 2)

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; třída HW {public void hello1 () {System.out.println ("ahoj z hello1"); } private void hello2 () {System.out.println ("ahoj z hello2"); }} public class MHD {public static void main (String [] args) throws Throwable {HW hw = new HW (); MethodHandles.Lookup lookup = MethodHandles.lookup (); MethodHandle mh = lookup.findVirtual (HW.class, "hello1", MethodType.methodType (void.class)); mh.invoke (hw); mh = lookup.findVirtual (HW.class, "hello2", MethodType.methodType (void.class)); }}

Výpis 2 deklaruje HW (Hello, World) a MHD třídy. HW prohlašuje a veřejnostahoj1 () metoda instance a soukroméahoj2 () metoda instance. MHD prohlašuje a hlavní() metoda, která se pokusí tyto metody vyvolat.

hlavní()Prvním úkolem je vytvořit instanci HW v rámci přípravy na vyvolání ahoj1 () a ahoj2 (). Dále získá vyhledávací objekt a použije tento objekt k získání popisovače metody pro vyvolání ahoj1 (). Tentokrát, MethodHandles.Lookupje findVirtual () metoda je volána a první argument předaný této metodě je a Třída objekt popisující HW třída.

Ukázalo se, že findVirtual () uspěje a následující mh.invoke (hw); výraz vyvolá ahoj1 (), což má za následek ahoj od ahoj1 je na výstupu.

Protože ahoj1 () je veřejnost, je přístupný pro hlavní() stránka volání metody. V porovnání, ahoj2 () není přístupný. Ve výsledku druhý findVirtual () vyvolání selže s IllegalAccessException.

Při spuštění této aplikace byste měli dodržovat následující výstup:

ahoj z hello1 Výjimka ve vlákně "main" java.lang.IllegalAccessException: člen je soukromý: HW.hello2 () void, z MHD na java.lang.invoke.MemberName.makeAccessException (MemberName.java:507) na java.lang. invoke.MethodHandles $ Lookup.checkAccess (MethodHandles.java:1172) na java.lang.invoke.MethodHandles $ Lookup.checkMethod (MethodHandles.java:1152) na java.lang.invoke.MethodHandles $ Lookup.accessVirtual: MethodHandles.java 648) na java.lang.invoke.MethodHandles $ Lookup.findVirtual (MethodHandles.java:641) na MHD.main (MHD.java:27)

Otázka: Výpisy 1 a 2 používají invokeExact () a vyvolat () metody k provedení popisovače metody. Jaký je rozdíl mezi těmito metodami?

A: Ačkoli invokeExact () a vyvolat () jsou navrženy k provedení popisovače metody (ve skutečnosti cílového kódu, na který odkazuje popisovač metody), liší se, pokud jde o provedení převodu typu na argumenty a návratovou hodnotu. invokeExact () neprovádí automatický převod kompatibilního typu na argumenty. Jeho argumenty (nebo výrazy argumentů) musí přesně odpovídat typu podpisu metody, přičemž každý argument je poskytován samostatně, nebo všechny argumenty poskytované společně jako pole. vyvolat () vyžaduje, aby jeho argumenty (nebo výrazy argumentů) byly typově kompatibilní s podpisem metody - provádějí se automatické převody typů, přičemž každý argument je poskytován samostatně, nebo jsou všechny argumenty poskytovány společně jako pole.

Otázka: Můžete mi poskytnout příklad, který ukazuje, jak vyvolat getter a setter pole instance?

A: Podívejte se na výpis 3.

Výpis 3. MHD.java (verze 3)

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; třída Point {int x; int y; } public class MHD {public static void main (String [] args) throws Throwable {MethodHandles.Lookup lookup = MethodHandles.lookup (); Bodový bod = nový Point (); // Nastavte pole xay. MethodHandle mh = lookup.findSetter (Point.class, "x", int.class); mh.invoke (bod 15); mh = lookup.findSetter (Point.class, "y", int.class); mh.invoke (bod 30); mh = lookup.findGetter (Point.class, "x", int.class); int x = (int) mh.invoke (bod); System.out.printf ("x =% d% n", x); mh = lookup.findGetter (Point.class, "y", int.class); int y = (int) mh.invoke (bod); System.out.printf ("y =% d% n", y); }}

Výpis 3 zavádí a Směřovat třída s dvojicí pojmenovaných 32bitových celých instančních polí X a y. Setter a getter každého pole je přístupný voláním MethodHandles.Lookupje findSetter () a findGetter () metody a výsledné MethodHandle je vrácen. Každý z findSetter () a findGetter () vyžaduje Třída argument, který identifikuje třídu pole, název pole a Třída objekt, který identifikuje podpis pole.

The vyvolat () metoda se používá k provedení setter nebo getter - v zákulisí jsou pole instance přístupná prostřednictvím JVM Putfield a getfield instrukce. Tato metoda vyžaduje, aby byl jako počáteční argument předán odkaz na objekt, jehož pole je přístupné. U vyvolávačů setteru musí být také předán druhý argument skládající se z hodnoty přiřazené poli.

Při spuštění této aplikace byste měli dodržovat následující výstup:

x = 15 y = 30

Otázka: Vaše definice popisovače metody zahrnuje frázi "s volitelnými transformacemi argumentů nebo návratových hodnot". Můžete uvést příklad transformace argumentů?

A: Vytvořil jsem příklad na základě Matematika třídy dvojitý prášek (dvojitý a, dvojitý b) třídní metoda. V tomto příkladu získám popisovač metody do pow () metoda a transformujte tento popisovač metody tak, aby byl předán druhý argument pow () je vždy 10. Podívejte se na výpis 4.