Machine virtuelle Java et CLR

140

Comme une sorte de suivi de la question intitulée Différences entre MSIL et le bytecode Java? , quelles sont les (principales) différences ou similitudes dans le fonctionnement de la machine virtuelle Java par rapport à laFramework .NET Common Language Runtime (CLR) fonctionne?

Aussi, est le Framework .NET CLR une "machine virtuelle" ou n'a-t-il pas les attributs d'une machine virtuelle?

Frank V
la source
Eh bien, si vous comparez des choses similaires et similaires, vous devez reformuler la question comme la différence entre la VM et le CLR (Common Language Runtime), qui est l'analogue direct de la VM.
cletus

Réponses:

278

Il y a beaucoup de similitudes entre les deux implémentations (et à mon avis: oui, ce sont toutes les deux des "machines virtuelles").

D'une part, ce sont toutes deux des VM basées sur la pile, sans notion de «registres» comme nous avons l'habitude de voir dans un processeur moderne comme le x86 ou PowerPC. L'évaluation de toutes les expressions ((1 + 1) / 2) est effectuée en poussant des opérandes sur la "pile", puis en faisant sortir ces opérandes de la pile chaque fois qu'une instruction (ajouter, diviser, etc.) a besoin de consommer ces opérandes. Chaque instruction repousse ses résultats sur la pile.

C'est un moyen pratique d'implémenter une machine virtuelle, car à peu près tous les processeurs du monde ont une pile, mais le nombre de registres est souvent différent (et certains registres sont à usage spécial, et chaque instruction attend ses opérandes dans différents registres, etc. ).

Donc, si vous voulez modéliser une machine abstraite, un modèle purement basé sur la pile est une très bonne solution.

Bien sûr, les vraies machines ne fonctionnent pas de cette façon. Ainsi, le compilateur JIT est chargé d'effectuer "l'enregistrement" des opérations de bytecode, en planifiant essentiellement les registres CPU réels pour contenir les opérandes et les résultats chaque fois que possible.

Donc, je pense que c'est l'un des plus grands points communs entre le CLR et la JVM.

Quant aux différences ...

Une différence intéressante entre les deux implémentations est que le CLR comprend des instructions pour créer des types génériques, puis pour appliquer des spécialisations paramétriques à ces types. Ainsi, au moment de l'exécution, le CLR considère un List <int> comme un type complètement différent d'un List <String>.

Sous les couvertures, il utilise le même MSIL pour toutes les spécialisations de type de référence (donc un List <String> utilise la même implémentation qu'un List <Object>, avec des types différents aux limites de l'API), mais chaque type de valeur utilise sa propre implémentation unique (List <int> génère un code complètement différent de List <double>).

En Java, les types génériques sont purement une astuce du compilateur. La machine virtuelle Java n'a aucune idée des classes qui ont des arguments de type et elle ne peut pas effectuer de spécialisations paramétriques au moment de l'exécution.

D'un point de vue pratique, cela signifie que vous ne pouvez pas surcharger les méthodes Java sur des types génériques. Vous ne pouvez pas avoir deux méthodes différentes, avec le même nom, différant uniquement selon qu'elles acceptent une List <String> ou une List <Date>. Bien sûr, puisque le CLR connaît les types paramétriques, il n'a aucun problème à gérer les méthodes surchargées sur les spécialisations de types génériques.

Au quotidien, c'est la différence que je remarque le plus entre le CLR et la JVM.

D'autres différences importantes incluent:

  • Le CLR a des fermetures (implémentées en tant que délégués C #). La JVM ne prend en charge les fermetures que depuis Java 8.

  • Le CLR a des coroutines (implémentées avec le mot clé C # 'yield'). La JVM ne le fait pas.

  • Le CLR permet au code utilisateur de définir de nouveaux types de valeur (structs), tandis que la JVM fournit une collection fixe de types de valeur (byte, short, int, long, float, double, char, boolean) et permet uniquement aux utilisateurs de définir une nouvelle référence- types (classes).

  • Le CLR prend en charge la déclaration et la manipulation des pointeurs. Ceci est particulièrement intéressant car la JVM et le CLR utilisent des implémentations strictes de ramasse-miettes de compactage générationnel comme stratégie de gestion de la mémoire. Dans des circonstances ordinaires, un GC compactage strict a vraiment du mal avec les pointeurs, car lorsque vous déplacez une valeur d'un emplacement mémoire à un autre, tous les pointeurs (et pointeurs vers des pointeurs) deviennent invalides. Mais le CLR fournit un mécanisme "d'épinglage" afin que les développeurs puissent déclarer un bloc de code dans lequel le CLR n'est pas autorisé à déplacer certains pointeurs. C'est très pratique.

  • La plus grande unité de code dans la JVM est soit un 'package' comme en témoigne le mot-clé 'protected' ou sans doute un JAR (c'est-à-dire Java ARchive) comme en témoigne la possibilité de spécifier un fichier jar dans le chemin de classe et de le traiter comme un dossier du code. Dans le CLR, les classes sont agrégées dans des «assemblys» et le CLR fournit une logique de raisonnement et de manipulation des assemblys (qui sont chargés dans «AppDomains», fournissant des sandbox au niveau des sous-applications pour l'allocation de mémoire et l'exécution de code).

  • Le format de bytecode CLR (composé d'instructions MSIL et de métadonnées) a moins de types d'instructions que la JVM. Dans la JVM, chaque opération unique (ajouter deux valeurs int, ajouter deux valeurs flottantes, etc.) a sa propre instruction unique. Dans le CLR, toutes les instructions MSIL sont polymorphes (ajoutez deux valeurs) et le compilateur JIT est responsable de la détermination des types d'opérandes et de la création du code machine approprié. Je ne sais pas quelle est la stratégie de préférence. Les deux ont des compromis. Le compilateur HotSpot JIT, pour la JVM, peut utiliser un mécanisme de génération de code plus simple (il n'a pas besoin de déterminer les types d'opérandes, car ils sont déjà codés dans l'instruction), mais cela signifie qu'il a besoin d'un format de bytecode plus complexe, avec plus de types d'instructions.

J'utilise Java (et j'admire la JVM) depuis une dizaine d'années maintenant.

Mais, à mon avis, le CLR est maintenant l'implémentation supérieure, à presque tous les égards.

benjismith
la source
73
Les fermetures et les générateurs sont implémentés au niveau du langage et sont simplement représentés comme des classes au niveau CLR.
Curt Hagenlocher
2
Qu'en est-il des différences dans la façon dont ils gèrent le tas? Le CLR dépend davantage du processus OS / hôte tandis que la machine virtuelle Java gère plus ou moins complètement la mémoire du tas.
Kelly S.Français
6
Une différence importante est le contraste entre la compilation juste à temps (CLR) et l'optimisation adaptative dans la JVM (Oracle / Sun).
Edwin Dalorzo
1
Les slots de variables locales de Java agissent comme des registres. Mais tout est de toute façon inutile puisque JIT transforme les slots locaux et la pile en de vrais registres.
Antimoine
1
@kuhajeyan c'est parce que lorsque CLR a été introduit, JVM avait 10 ans. cela fait longtemps en informatique. Lorsque JVM est arrivé en 1993, il n'y avait pas de prétendant sérieux, pour CLR (2003) il y avait une JVM mature et solide avec une forte implantation dans l'industrie.
Simple Fellow
25

Votre première question est de comparer la JVM avec le .NET Framework - je suppose que vous vouliez plutôt comparer avec le CLR. Si c'est le cas, je pense que vous pourriez écrire un petit livre à ce sujet ( EDIT: on dirait que Benji a déjà :-)

Une différence importante est que le CLR est conçu pour être une architecture indépendante du langage, contrairement à la JVM.

Une autre différence importante est que le CLR a été spécialement conçu pour permettre un haut niveau d'interopérabilité avec le code natif. Cela signifie que le CLR doit gérer la fiabilité et la sécurité lors de l'accès et de la modification de la mémoire native, et également gérer le rassemblement entre les structures de données CLR et les structures de données natives.

Pour répondre à votre deuxième question, le terme «machine virtuelle» est un terme plus ancien du monde matériel (par exemple la virtualisation de la 360 par IBM dans les années 1960) qui signifiait autrefois une émulation logicielle / matérielle de la machine sous-jacente pour accomplir le même type de ce que fait VMWare.

Le CLR est souvent appelé "moteur d'exécution". Dans ce contexte, il s'agit d'une implémentation d'une IL Machine sur un x86. C'est également ce que fait la JVM, bien que vous puissiez affirmer qu'il existe une différence importante entre les bytecodes polymorphes du CLR et les bytecodes typés de la JVM.

La réponse pédante à votre deuxième question est donc «non». Mais cela dépend vraiment de la façon dont vous définissez ces deux termes.

EDIT: Une autre différence entre la JVM et le CLR est que la JVM (version 6) est très réticente à libérer la mémoire allouée au système d'exploitation, même là où elle le peut.

Par exemple, disons qu'un processus JVM démarre et alloue initialement 25 Mo de mémoire à partir du système d'exploitation. Le code de l'application tente ensuite les allocations qui nécessitent 50 Mo supplémentaires. La JVM allouera 50 Mo supplémentaires du système d'exploitation. Une fois que le code d'application a cessé d'utiliser cette mémoire, il est récupéré et la taille du tas JVM diminuera. Cependant, la JVM ne libérera la mémoire allouée du système d'exploitation que dans certaines circonstances très spécifiques . Sinon, pour le reste de la durée de vie du processus, cette mémoire restera allouée.

Le CLR, d'autre part, libère la mémoire allouée au système d'exploitation si elle n'est plus nécessaire. Dans l'exemple ci-dessus, le CLR aurait libéré la mémoire une fois que le tas avait diminué.

HTTP 410
la source
2
Il n'est absolument pas correct que la JVM ne libère pas la mémoire allouée. Voir ma réponse à cette question pour preuve: stackoverflow.com/questions/366658/…
Michael Borgwardt
J'ai vu la mémoire JVM retourner à Windows.
Steve Kuo
J'ai changé ma réponse pour dire que la JVM 6 est très réticente à libérer de la mémoire, avec des liens vers les réponses de Ran et Michael. Je n'ai jamais vu ce comportement avec JVM 5, alors peut-être que cette version était encore plus réticente.
HTTP 410
Pourriez-vous expliquer comment la JVM gère activement le tas tandis que le CLR s'appuie sur le processus parent? L'exemple spécifique que j'utilise est que la machine virtuelle Java a des arguments d'exécution pour la taille maximale du tas, contrairement à l'environnement CLR par défaut. S'il est vrai qu'une application CLR hébergée sous IIS peut configurer IIS pour limiter la mémoire, cela signifierait inclure IIS dans la définition de la machine virtuelle.
Kelly
@Steve Kuo, oui j'ai vu ça aussi. généralement entre 17 h et 18 h.
Simple Fellow
11

Le CLR et la JVM sont tous deux des machines virtuelles.

Le .NET Framework et l'environnement Java Runtime sont le regroupement des machines virtuelles respectives et de leurs bibliothèques. Sans bibliothèques, les VM sont plutôt inutiles.

Allain Lalonde
la source
11

Plus de détails sur les différences peuvent être trouvés à partir de diverses sources universitaires et privées. Un bon exemple est CLR Design Choices .

Quelques exemples spécifiques incluent:

  • Certains opperands de bas niveau sont typés comme "ajouter deux entiers" où CLR utilise un opérande polymorphe. (ie fadd / iadd / ladd vs juste ajouter)
  • Actuellement, la JVM effectue un profilage et une optimisation d'exécution plus agressifs (c'est-à-dire Hotspot). CLR effectue actuellement des optimisations JIT, mais pas l'optimisation d'exécution (c'est-à-dire remplacer le code pendant que vous exécutez).
  • CLR n'intègre pas de méthodes virtuelles, JVM le fait ...
  • Prise en charge des types valeur dans le CLR au-delà des "primitives".
James Schek
la source
-11

Ce n'est pas une machine virtuelle, le framework .net compile les assemblys en binaire natif au moment de la première exécution:

En informatique, la compilation juste à temps (JIT), également appelée traduction dynamique, est une technique permettant d'améliorer les performances d'exécution d'un programme informatique. JIT s'appuie sur deux idées antérieures dans les environnements d'exécution: la compilation bytecode et la compilation dynamique. Il convertit le code au moment de l'exécution avant de l'exécuter de manière native, par exemple le bytecode en code machine natif. L'amélioration des performances par rapport aux interpréteurs provient de la mise en cache des résultats de la traduction de blocs de code, et pas simplement de la réévaluation de chaque ligne ou opérande à chaque fois qu'il est rencontré (voir Langage interprété). Il présente également des avantages par rapport à la compilation statique du code au moment du développement, car il peut recompiler le code si cela s'avère avantageux, et peut être en mesure d'appliquer des garanties de sécurité.

Plusieurs environnements d'exécution modernes, tels que .NET Framework de Microsoft, la plupart des implémentations de Java et, plus récemment, Actionscript 3, reposent sur la compilation JIT pour l'exécution de code à grande vitesse.

Source: http://en.wikipedia.org/wiki/Just-in-time_compilation

L'ajout du framework .NET contient une machine virtuelle, tout comme Java.

Diones
la source
10
Ce n'est pas parce que la machine virtuelle utilise JIT pour l'optimisation des performances que ce n'est plus une machine virtuelle. Lorsque le programmeur compile, il compile vers la machine virtuelle, laissant à l'implémentation le soin d'effectuer l'exécution comme bon lui semble
Allain Lalonde