Pourquoi la JVM ne cache-t-elle pas le code compilé JIT?

107

L'implémentation JVM canonique de Sun applique une optimisation assez sophistiquée au bytecode pour obtenir des vitesses d'exécution quasi-natives après que le code a été exécuté plusieurs fois.

La question est, pourquoi ce code compilé n'est-il pas mis en cache sur le disque pour une utilisation lors des utilisations ultérieures de la même fonction / classe?

Dans l'état actuel des choses, chaque fois qu'un programme est exécuté, le compilateur JIT démarre à nouveau, plutôt que d'utiliser une version précompilée du code. L'ajout de cette fonctionnalité n'ajouterait-il pas une augmentation significative de la durée d'exécution initiale du programme, lorsque le bytecode est essentiellement interprété?

Chinmay Kanchi
la source
4
Un fil de discussion sur ce problème: javalobby.org/forums/thread.jspa?threadID=15812
miku
2
Mais une question peu probable pour attirer une réponse définitive.
bmargulies
1
Je ne suis pas sûr d'un coup de pouce "significatif", car il faudrait alors charger des éléments JITted à partir du disque au lieu de les JITer en mémoire. Cela pourrait accélérer les choses, mais au cas par cas.
R. Martinho Fernandes
1
Merci pour les bonnes réponses à tous! Toutes les réponses étaient également valables, alors je suis allé avec la communauté sur celui-ci ...
Chinmay Kanchi
C'est une bonne question si vous me posez :)
Alfred

Réponses:

25

Sans recourir au cut'n'paste du lien que @MYYN a posté, je soupçonne que c'est parce que les optimisations effectuées par la JVM ne sont pas statiques, mais plutôt dynamiques, basées sur les modèles de données ainsi que sur les modèles de code. Il est probable que ces modèles de données changent pendant la durée de vie de l'application, rendant les optimisations mises en cache moins qu'optimales.

Vous auriez donc besoin d'un mécanisme pour déterminer si les optimisations enregistrées étaient toujours optimales, à quel point vous pourriez tout aussi bien ré-optimiser à la volée.

skaffman
la source
6
... ou vous pouvez simplement proposer la persistance en option , comme le fait la JVM d'Oracle - permettre aux programmeurs avancés d'optimiser les performances de leur application quand et où ils savent que les modèles ne changent pas , sous leur responsabilité. Pourquoi pas?!
Alex Martelli
2
Parce que ça n'en vaut probablement pas la peine. Si ni SUN, IBM ni BEA ne jugeaient que cela valait la peine pour leurs performances JVM, il y aura une bonne raison à cela. Peut-être que leur optimisation RT est plus rapide que celle d'Oracle, c'est pourquoi Oracle la met en cache.
skaffman
9
Pourquoi ne pas prendre les optimats stockés comme point de départ, pour utiliser ce qui a été appris lors des exécutions précédentes? À partir de là, JIT pourrait fonctionner comme d'habitude pour ré-optimiser les choses. À l'arrêt, ce code peut être conservé à nouveau et utilisé dans la prochaine exécution comme nouveau point de départ.
Puce
1
@Puce La seule raison à laquelle je peux penser est qu'AFAIK vous n'obtient aucune statistique de profilage en exécutant du code optimisé. Donc, vous n'auriez aucun moyen d'améliorer ...
maaartinus
1
Personnellement, je serais bien avec une option "il suffit de conserver les informations de profilage JIT entre les exécutions" avec tous les avertissements que "cela ne sera valide qu'avec exactement la même JVM, les mêmes données, etc. et sinon ignoré". En ce qui concerne la raison pour laquelle cela n'a pas été mis en œuvre, je m'attendrais à ce que la complexité supplémentaire de la persistance et de la validation des données de base JIT soit trop importante pour prendre des ressources d'autres projets. Étant donné le choix entre celui-ci et les flux lambda + Java 8, je préférerais ce dernier.
Thorbjørn Ravn Andersen
25

La JVM d'Oracle est en effet documentée pour le faire - citant Oracle,

le compilateur peut tirer parti du modèle de résolution de classe d'Oracle JVM pour éventuellement conserver les méthodes Java compilées dans les appels, sessions ou instances de base de données. Une telle persistance évite la surcharge des recompilations inutiles entre les sessions ou les instances, quand on sait que sémantiquement le code Java n'a pas changé.

Je ne sais pas pourquoi toutes les implémentations sophistiquées de VM n'offrent pas d'options similaires.

Alex Martelli
la source
8
Parce que d'autres JVM sophistiqués n'ont pas un grand SGBDR d'entreprise à portée de main pour stocker des choses :)
skaffman
Hou la la! cela signifie que les compilations sont parfois mises en cache. C'est une bonne nouvelle!
Sandeep Jindal
Le J9 d'IBM est également documenté pour le faire.
user314104
9
Notez que cette JVM Oracle est celle qui se trouve dans la base de données Oracle, et non le téléchargement qu'Oracle a obtenu lors de l'achat de Sun.
Thorbjørn Ravn Andersen
14

Une mise à jour des réponses existantes - Java 8 a un JEP dédié à résoudre ce problème:

=> JEP 145: Code compilé en cache . Nouveau lien .

À un niveau très élevé, son objectif déclaré est :

Enregistrez et réutilisez le code natif compilé des exécutions précédentes afin d'améliorer le temps de démarrage des grandes applications Java.

J'espère que cela t'aides.

Eugen
la source
3
La fonctionnalité n'a pas atteint la version finale .
assylias
5

Excelsior JET dispose d'un compilateur JIT de mise en cache depuis la version 2.0, publiée en 2001. De plus, son compilateur AOT peut recompiler le cache en un seul objet DLL / partagé en utilisant toutes les optimisations.

Dmitry Leskov
la source
3
Oui, mais la question portait sur la JVM canonique, c'est-à-dire la JVM de Sun. Je suis bien conscient qu'il existe plusieurs compilateurs AOT pour Java ainsi que d'autres JVM de mise en cache.
Chinmay Kanchi
0

Je ne connais pas les raisons réelles, n'étant aucunement impliqué dans l'implémentation de la JVM, mais je peux penser à quelques-unes plausibles:

  • L'idée de Java est d'être un langage à écriture unique, exécutable n'importe où, et mettre des éléments précompilés dans le fichier de classe enfreint en quelque sorte cela (seulement "en quelque sorte" car bien sûr le code d'octet réel serait toujours là)
  • Cela augmenterait la taille des fichiers de classe car vous y auriez le même code plusieurs fois, surtout si vous exécutez le même programme sous plusieurs JVM différentes (ce qui n'est pas vraiment rare, lorsque vous considérez différentes versions comme des JVM différentes, ce que vous vraiment à faire)
  • Les fichiers de classe eux-mêmes peuvent ne pas être inscriptibles (bien qu'il soit assez facile de vérifier cela)
  • Les optimisations de la JVM sont partiellement basées sur les informations d'exécution et sur d'autres exécutions, elles peuvent ne pas être aussi applicables (bien qu'elles devraient toujours fournir certains avantages)

Mais je devine vraiment, et comme vous pouvez le voir, je ne pense pas vraiment qu'aucune de mes raisons ne soit de véritables bouchons. Je pense que Sun ne considère tout simplement pas ce support comme une priorité, et peut-être que ma première raison est proche de la vérité, car cela pourrait aussi amener les gens à penser que les fichiers de classe Java ont vraiment besoin d'une version séparée pour chaque VM au lieu d'être multiplateforme.

Ma méthode préférée serait en fait d'avoir un traducteur de bytecode vers natif séparé que vous pourriez utiliser pour faire quelque chose comme ça explicitement à l'avance, en créant des fichiers de classe qui sont explicitement construits pour une VM spécifique, avec éventuellement le bytecode d'origine en eux afin que vous peut également fonctionner avec différentes VM. Mais cela vient probablement de mon expérience: j'ai surtout fait Java ME, où ça fait vraiment mal que le compilateur Java ne soit pas plus intelligent en matière de compilation.

JaakkoK
la source
1
il y a une place dans le fichier de classe pour de telles choses, en fait c'était l'intention d'origine (stocker le code JIT comme attribut dans le fichier de classe).
TofuBeer
@TofuBeer: Merci pour la confirmation. Je soupçonnais que cela pourrait être le cas (c'est ce que j'aurais fait), mais je n'étais pas sûr. Modifié pour supprimer cela comme raison possible.
JaakkoK
Je pense que vous avez frappé dans le mille avec votre dernier point. Les autres pourraient être contournés, mais cette dernière partie est, je pense, la principale raison pour laquelle le code JITed n'est pas conservé.
Sasha Chedygov
1
Le dernier paragraphe sur le compilateur bytecode-to-native explicite est ce que vous avez actuellement dans .NET avec NGEN ( msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx ).
R. Martinho Fernandes