Vilka problem har man historiskt sett haft med att kompilera dynamiska språk till Java bytekod? Vad fanns för work-arounds? Vilka andra roliga saker kan man göra själv med Invokedynamic?
Även om du bara har en vag bild på vad en bytekod överhuvudtaget är och varför du skall bry dig om en ny sådan så är det här en presentation som kommer att fördjupa din förståelse av Java på riktigt låg nivå.
2. The following is intended to outline our general
product direction. It is intended for information
purposes only, and may not be incorporated into
any contract. It is not a commitment to deliver any
material, code, or functionality, and should not
be relied upon in making purchasing decisions.
The development, release, and timing of any
features or functionality described for Oracle’s
products remains at the sole discretion of Oracle.
2
4. Intro till JSR-292: invokedynamic,
MethodHandle och dynamiskt typade
språk på JVM:en
<Insert Picture Here>
• Varför JSR-292/invokedynamic?
• Vad erbjuder JSR-292?
• Dynamiskt typade exempel och jämförelser med vad
JVM:en kan göra av Java
4
6. • Det finns en mängd intressanta programmeringsspråk
• JVM:en är långt mycket mer avancerad än andra
VM:ar/runtimes
• Java är inte allas favoritspråk
• Naturligvis vill man köra alla nya spännande språk på
JVM:en
6
7. • Dynamiskt typade språk:
– Modifierar klasser vid körtid
– Alternativt, saknar klasser och lägger till properties i körtid
class ExampleClass
def exampleMethod
5
end
end
exampleObject.class.send(:define_method,
"aNewMethod") do
10
end
7
8. • Motsvarigheten i Java finns egentligen inte
– Den naiva implementationen blir istället en Map<String,
TargetMethodObject>
• Mer eller mindre avancerade sätt att optimera
ovanstående
– JRuby har sina fält i en Object[] och en Map<String, Integer>
där du slår upp vilket index du ska använda in i arrayen
8
9. • Dynamiskt typade språk:
– har ofta inkompatibla sätt att definera/anropa metoder
(defun foo
(var...
&optional (var initform svar)...
&rest var
&key ((keyword var) initform svar)...
&aux (var initform)...))
9
10. • Man kan arbeta runt problemen
• Ett av de få verktyg man har är
klassladdning/klassurladdning
– leder till permgen-problem på HotSpot
• Det tar tid att implementera
– om man vill få det att gå fort
10
11. Invokedynamic, en språkimplementatörs räddare i
nöden
• Invokedynamic är en brygga mellan
språkimplementatören och JVM:en
• Invokedynamic består av 3 delar
– en ny bytekod som markerar ett dynamiskt CallSite
– ett protokoll/API för livscykeln av dynamiska CallSites
– MethodHandle
• funktionspekare på sterodier
11
12. • Första gången JVM:en exekverar en invokedynamic
bytekod kommer den:
– Hitta och köra den bootstrapmetod som skaparen av
klassfilen angett
• resulterar i ett CallSite-objekt som har ett MethodHandle
som target
– Länka CallSite-objektet
• Ovanstående görs bara en gång
• Efterföljande körningar av samma invokedynamic
kommer helt skippa bootstrap och länkning
• == Snabbt!!
12
14. • En invokedynamic kommer alltid att motsvaras av
samma CallSite
• Ett CallSite kan ändra target till att peka på en ny
MethodHandle
• Om du kan uttrycka ditt språks semantik som CallSite
+ en utbytbar MethodHandle kommer det gå fort!
14
15. MethodHandles is the kitchen sink of the JVM
- Dan Heidinga, JVM Lang 2010
• MethodHandle är i grund och botten en
funktionspekare
• Pekas normalt ut som target i en CallSite
• Kan också köras genom ett Java-API
• Hjälpklassen ”MethodHandles” har metoder för att
– Wrappa MethodHandle i andra MethodHandle
– Transformera argumenten på ett MethodHandle
– Köra kod runt en MethodHandle
15
16. • MethodHandle kan också peka ut fält
• Användbart för lat initialisering av en gigantisk array
– Wrappa arrayaccessen i ett MethodHandle som transparent
initialiserar den
– Generera bytecode som kör MethodHandlet för den
wrappade arrayen istället för den vanliga ”aref”-bytekoden
16
17. • MethodHandle kan också transformera argument
– asCollector(…)
– asFixedArity(…)
– asSpreader(…)
– …
• Och byta returtyp
– asType(newType)
17
18. • MethodHandles.Lookup har också stöd för att
reflektera över typer
• Användbart för det mesta man vill göra med reflection
• Fast snabbt
18
19. • JVM:en vet hur den skall optimera MethodHandle
• Igen: Kan du bara uttrycka ditt språks semantik som
CallSite + en utbytbar MethodHandle kommer det gå
fort!
19
20. Exempel: ett vanligt metodanrop i Java
• Ett call i Java kommer kompileras till:
– Slå upp objektets vtable (en del av klassen)
– Kör koden som pekas ut på en vid just-in-time-kompilering
känd plats i vtablen
moveq r12, [rdi + 0x128] ;get vtable for ”this”
callq [r12+0x12] ;call 3:rd method
• 2 instruktioner + ev. några cachemissar
• == Snabbt!
20
21. Exempel: ett metodanrop i fiktiv Ruby på JVM:en
• Motsvarande logik för ett dynamiskt typat språk utan
invokedynamic:
String methodName;
CallTarget t;
for(RubyClazz clz : o.getClzAndSuperClzs()) {
if ((t = clz.hasMethod(methodName)) != null)
break;
}
if (t == null) throw new NoSuchRubyMethod();
21
22. Exempel: med invokedynamic …
• Låt bootstrapmetoden returnera ett MethodHandle:
MethodHandle lastCalled;
int rubyClzId;
if (receiver.currentRubyClzId == rubyClzId) {
return lastCalled.invoke(args …);
} else {
missCount++;
MethodHandle t = doFulLookup(mName, args…);
if (missCount > MISS_LIMIT) {
relinkThisCallSite(t);
}
return t.invoke();
}
22
23. Exempel: med invokedynamic …
• Bootstrapmetoden får leta reda på den
implementerande ruby-metoden
• Wrappa i ett MethodHandle som
– Testar om det är samma implementerande ruby-klass
– Testar om det är samma version av ruby-klassen
• Så länge det är samma implementerande klass
kommer JVM:en kunna kompilera till en handfull
instruktioner
• Det är nästan alltid samma implementerande klass!
• Om det inte är samma klass kan din ruby-VM begära
omlänkning via CallSite-objektet!
23
25. • Invokedynamic hjälper dig som språkimplementatör:
– Kommunicera ditt språks speciella semantik till VM:en
– Återanvända VM:ens maskineri för
• Optimeringar
• Deoptimeringar
– Hjälper dig att transformera semantiken i ditt språks
objektsystem till något som JVM:en kan använda
25