Warum zwischenspeichert die JVM keinen JIT-kompilierten Code?

107

Die kanonische JVM-Implementierung von Sun wendet eine ziemlich ausgefeilte Optimierung auf Bytecode an, um nahezu native Ausführungsgeschwindigkeiten zu erhalten, nachdem der Code einige Male ausgeführt wurde.

Die Frage ist, warum dieser kompilierte Code nicht zur späteren Verwendung derselben Funktion / Klasse auf der Festplatte zwischengespeichert wird.

So wie es aussieht, wird der JIT-Compiler jedes Mal, wenn ein Programm ausgeführt wird, neu gestartet, anstatt eine vorkompilierte Version des Codes zu verwenden. Würde das Hinzufügen dieser Funktion die anfängliche Laufzeit des Programms nicht erheblich verbessern, wenn der Bytecode im Wesentlichen interpretiert wird?

Chinmay Kanchi
quelle
4
Ein Thread, der dieses Problem diskutiert: javalobby.org/forums/thread.jspa?threadID=15812
miku
2
Aber eine unwahrscheinliche Frage, die eine endgültige Antwort findet.
Bmargulies
1
Ich bin mir nicht sicher über einen "signifikanten" Boost, da Sie dann JITted-Daten von der Festplatte laden müssten, anstatt sie im Arbeitsspeicher zu speichern. Es könnte die Dinge beschleunigen, aber von Fall zu Fall.
R. Martinho Fernandes
1
Vielen Dank für die tollen Antworten an alle! Alle Antworten waren gleichermaßen gültig, also ging ich mit der Community auf diese ...
Chinmay Kanchi
Dies ist eine gute Frage, wenn Sie mich fragen :)
Alfred

Antworten:

25

Ohne auf das Ausschneiden und Einfügen des von @MYYN geposteten Links zurückzugreifen, liegt dies vermutlich daran, dass die von der JVM durchgeführten Optimierungen nicht statisch, sondern dynamisch sind und sowohl auf den Datenmustern als auch auf den Codemustern basieren. Es ist wahrscheinlich, dass sich diese Datenmuster während der Lebensdauer der Anwendung ändern und die zwischengespeicherten Optimierungen nicht optimal sind.

Sie benötigen also einen Mechanismus, um festzustellen, ob die gespeicherten Optimierungen noch optimal sind. Zu diesem Zeitpunkt können Sie sie auch im laufenden Betrieb erneut optimieren.

Skaffman
quelle
6
... oder Sie können einfach Persistenz als Option anbieten , wie es die JVM von Oracle tut - und fortgeschrittene Programmierer in die Lage versetzen, die Leistung ihrer Anwendung zu optimieren, wenn und wo sie nur wissen, dass sich die Muster unter ihrer Verantwortung nicht ändern. Warum nicht?!
Alex Martelli
2
Weil es sich wahrscheinlich nicht lohnt. Wenn weder SUN, IBM noch BEA es für ihre Performance-JVMs als lohnenswert erachten, wird es einen guten Grund dafür geben. Möglicherweise ist ihre RT-Optimierung schneller als die von Oracle, weshalb Oracle sie zwischenspeichert.
Skaffman
9
Warum nicht gespeicherte Optimierungen als Ausgangspunkt nehmen, um das zu nutzen, was in früheren Läufen gelernt wurde? Von dort aus konnte JIT wie gewohnt arbeiten und Sachen neu optimieren. Beim Herunterfahren kann dieser Code erneut beibehalten und im nächsten Lauf als neuer Ausgangspunkt verwendet werden.
Puce
1
@Puce Der einzige Grund, an den ich denken kann, ist, dass Sie mit AFAIK keine Profilstatistiken erhalten, wenn Sie optimierten Code ausführen. Sie hätten also keine Möglichkeit, sich zu verbessern ...
Maaartinus
1
Ich persönlich wäre in Ordnung mit der Option "Behalten Sie die JIT-Profilinformationen zwischen den Läufen einfach bei" mit allen Warnungen, dass "dies nur mit genau derselben JVM, denselben Daten usw. gültig ist und ansonsten ignoriert wird". In Bezug darauf, warum dies nicht implementiert wurde, würde ich erwarten, dass die zusätzliche Komplexität des Fortbestehens und Validierens der JIT-Seed-Daten zu groß war, um Ressourcen aus anderen Projekten zu entnehmen. Angesichts der Wahl zwischen diesem und Java 8 Lambda + Streams hätte ich lieber letzteres.
Thorbjørn Ravn Andersen
25

Die JVM von Oracle ist in der Tat dafür dokumentiert - unter Angabe von Oracle,

Der Compiler kann das Klassenauflösungsmodell von Oracle JVM nutzen, um kompilierte Java-Methoden optional über Datenbankaufrufe, Sitzungen oder Instanzen hinweg beizubehalten. Durch diese Persistenz wird der Aufwand für unnötige Neukompilierungen über Sitzungen oder Instanzen hinweg vermieden, wenn bekannt ist, dass sich der Java-Code semantisch nicht geändert hat.

Ich weiß nicht, warum alle hoch entwickelten VM-Implementierungen keine ähnlichen Optionen bieten.

Alex Martelli
quelle
8
Weil andere hoch entwickelte JVMs kein hupendes, großartiges Unternehmens-RDBMS zur
Hand haben,
Beeindruckend! Das bedeutet, dass die Zusammenstellungen manchmal zwischengespeichert werden. Das sind gute Neuigkeiten!
Sandeep Jindal
Dazu ist auch IBMs J9 dokumentiert.
user314104
9
Beachten Sie, dass diese Oracle-JVM diejenige in der Oracle-Datenbank ist und nicht der Download, den Oracle beim Kauf von Sun erhalten hat.
Thorbjørn Ravn Andersen
14

Eine Aktualisierung der vorhandenen Antworten - Java 8 verfügt über einen JEP, der sich der Lösung dieses Problems widmet:

=> JEP 145: Cache-kompilierten Code . Neuer Link .

Auf sehr hohem Niveau ist das erklärte Ziel :

Speichern und wiederverwenden Sie kompilierten nativen Code aus früheren Läufen, um die Startzeit großer Java-Anwendungen zu verbessern.

Hoffe das hilft.

Eugen
quelle
3
Das Feature hat es nicht bis zur endgültigen Veröffentlichung geschafft .
Assylias
5

Excelsior JET verfügt seit der 2001 veröffentlichten Version 2.0 über einen Caching-JIT-Compiler. Darüber hinaus kann der AOT-Compiler den Cache unter Verwendung aller Optimierungen in eine einzelne DLL / ein gemeinsames Objekt neu kompilieren.

Dmitry Leskov
quelle
3
Ja, aber die Frage betraf die kanonische JVM, dh die JVM von Sun. Mir ist klar, dass es mehrere AOT-Compiler für Java sowie andere Caching-JVMs gibt.
Chinmay Kanchi
0

Ich kenne die tatsächlichen Gründe nicht, da ich in keiner Weise an der JVM-Implementierung beteiligt bin, aber ich kann mir einige plausible vorstellen:

  • Die Idee von Java ist es, eine Sprache zu schreiben, die einmal überall ausgeführt werden kann, und vorkompiliertes Material in die Klassendatei einzufügen, verstößt dagegen (nur "irgendwie", da natürlich der eigentliche Bytecode noch vorhanden wäre).
  • Dies würde die Größe der Klassendateien erhöhen, da Sie dort mehrmals denselben Code haben würden, insbesondere wenn Sie dasselbe Programm unter mehreren verschiedenen JVMs ausführen (was nicht wirklich ungewöhnlich ist, wenn Sie verschiedene Versionen als unterschiedliche JVMs betrachten, die Sie verwenden muss wirklich tun)
  • Die Klassendateien selbst sind möglicherweise nicht beschreibbar (obwohl es ziemlich einfach wäre, dies zu überprüfen).
  • Die JVM-Optimierungen basieren teilweise auf Laufzeitinformationen und auf anderen Läufen sind sie möglicherweise nicht so zutreffend (obwohl sie dennoch einige Vorteile bieten sollten).

Aber ich vermute wirklich, und wie Sie sehen können, glaube ich nicht, dass einer meiner Gründe tatsächliche Show-Stopper sind. Ich denke, Sun betrachtet diese Unterstützung einfach nicht als Priorität, und vielleicht liegt mein erster Grund nahe an der Wahrheit, da dies gewöhnlich auch dazu führt, dass die Leute denken, dass Java-Klassendateien wirklich eine separate Version für jede VM benötigen, anstatt zu sein plattformübergreifend.

Mein bevorzugter Weg wäre tatsächlich, einen separaten Bytecode-zu-Native-Übersetzer zu haben, mit dem Sie so etwas explizit im Voraus ausführen können, um Klassendateien zu erstellen, die explizit für eine bestimmte VM erstellt wurden und möglicherweise den ursprünglichen Bytecode enthalten kann auch mit verschiedenen VMs ausgeführt werden. Aber das kommt wahrscheinlich aus meiner Erfahrung: Ich habe hauptsächlich Java ME gemacht, wo es wirklich weh tut, dass der Java-Compiler beim Kompilieren nicht schlauer ist.

JaakkoK
quelle
1
In der Klassendatei gibt es eine Stelle für solche Dinge, die ursprünglich beabsichtigt war (speichern Sie den JIT-Code als Attribut in der Klassendatei).
TofuBeer
@TofuBeer: Danke für die Bestätigung. Ich vermutete, dass dies der Fall sein könnte (das hätte ich getan), war mir aber nicht sicher. Bearbeitet, um dies als möglichen Grund zu entfernen.
JaakkoK
Ich denke, Sie haben mit Ihrem letzten Stichpunkt den Nagel auf den Kopf getroffen. Die anderen könnten umgangen werden, aber dieser letzte Teil ist meiner Meinung nach der Hauptgrund, warum JITed-Code nicht beibehalten wird.
Sasha Chedygov
1
Der letzte Absatz über den expliziten Bytecode-zu-Native-Compiler ist der, den Sie derzeit in .NET mit NGEN haben ( msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx ).
R. Martinho Fernandes