Was ist neu in Java 7? Teil 3 – Allgemeingültigkeit

JSR 292

Antwort durch Java 7

Neu sind die beschriebenen Herausforderungen nicht. Mit dem JSR 223 (Scripting for the Java Platform) wurde schon in Java 6 versucht, Script-Sprachen an Java anzubinden, indem Script-Engines den Code der gewünschten Sprache zur Laufzeit ausführten. Neben Mozillas Rhino als Referenzimplementierung für JavaScript ließen sich Ruby, PHP und Groovy einsetzen. Dieser Ansatz half zwar bei der Anbindung unterschiedlicher Sprachen, die Probleme beim Compiler-Bau blieben jedoch unverändert.

Die Verwendung der JVM als Laufzeitumgebung für andere Programmiersprachen geht mit Java 7 der JSR 292 (Supporting Dynamically Typed Languages on the Java Platform) an. Java 7 hat zum ersten Mal in der Geschichte von Java neuen Bytecode mit an Bord: Mit der neuen Anweisung InvokeDynamic lassen sich Methoden ohne vorherige Prüfung aufrufen, zu welcher Klasse die Methode gehört, von welchem Typ ihr Rückgabewert ist oder welche Methodenparameter sie verwendet. Mit ihrer Hilfe lässt sich die Verbindung zwischen dem Aufrufer und der Methoden-Implementierung anpassen. Ausgangspunkt ist dabei die Aufrufer-Seite (call site). Dabei wird eine "InvokeDynamic call site" durch eine Bootstrap-Methode an eine Methode gebunden. Das erfolgt einmalig. Muss die JVM jetzt eine InvokeDynamic-Instruktion (bspw. "+") ausführen und kennt sie eine entsprechende Implementierung addiereZwei(Integer, Integer) in einer Klasse, lässt sich diese verlinken:

public static CallSite bootstrap(
MethodHandles.Lookup callerClass, String dynMethodName,
MethodType dynMethodType)
throws Throwable {

MethodHandle mh =
callerClass.findStatic(
Beispiel.class,
"MeineTypen.addiereZwei",
MethodType.methodType(Integer.class, Integer.class, Integer.class));

if (!dynMethodType.equals(mh.type())) {
mh = mh.asType(dynMethodType);
}

return new ConstantCallSite(mh);
}

Dabei stellt die Klasse MeineTypen einen Teil des Typsystems der dynamischen Sprache dar. Die abgebildete Beispielklasse übernimmt das Bootstrapping der Verbindung. Beachtenswert ist es, dass in dem Beispiel alle Aufrufe und die Methode "addiereZwei" davon ausgehen, dass es sich um Argumente vom Typ "Integer" handelt. In einer konkreten Implementierung wäre hierbei mit Rückfallmöglichkeiten die entsprechende Varianz der Typen zu berücksichtigen. Im Falle von JRuby sieht der generierte Bytecode mit der Anweisung
InvokeDynamic folgendermaßen aus.

aload_1       
aload_2
aload 10
ldc #67 // String +
aload 11
invokedynamic #76, 0 // InvokeDynamic
// #1:call:( (...)[invocationBootstrap(...)]
areturn

Auch wenn das Codebeispiel den Eindruck erweckt, dass die neue Funktion dem Java-Entwickler zur Verfügung steht, ist dem tatsächlich nicht so. Es handelt sich zwar um Klassen des JDK, diese sind allerdings lediglich für einen Implementierer von JVM-Sprachen hilfreich. Am nächsten kommt man dem neuen Bytecode noch beim Einsatz von Bytecode-Manipulation oder -Analyse. Das ASM-Framework ist hierfür sicherlich ein bekannteres Beispiel. Da die neue Bytecode-Anweisung für Java in Summe nicht notwendig ist, kommt sie auch nicht im Java-Compiler zum Einsatz.

Für den Hauptentwickler von JRuby, Charles Oliver Nutter, stellt gerade diese Neuerung allerdings eine echte Revolution dar. Durch den starken Gebrauch von InvokeDynamic ließen sich offenbar Performancegewinne von bis zu 200 Prozent erzielen. Details über die Verwendung von InvokeDynamic bei JRuby finden sich auch in Nutters Slides eines auf dem JVM Language Summit gehaltenen Vortrags (PDF). Für Java 8 ist das als Grundlagenarbeit zu verstehen. Die für die kommende Version geplanten Closures für Java (Project Lambda) werden nach aktueller Planung ebenfalls mit der InvokeDynamic-Anweisung arbeiten, wie Brian Goetz, Oracles Architekt für die Java-Sprache, auf der gleichen Veranstaltung aufgezeigt hat (PDF).