Javas virtuelle Maschine und CLR


140

Als eine Art Antwort auf die Frage namens Unterschiede zwischen MSIL und Java-Bytecode? Was sind die (Haupt-) Unterschiede oder Ähnlichkeiten in der Funktionsweise der Java Virtual Machine im Vergleich zur Funktionsweise der Java Virtual Machine?.NET Framework Common Language Runtime (CLR) funktioniert?

Auch ist das .NET Framework CLR eine "virtuelle Maschine" oder hat sie nicht die Attribute einer virtuellen Maschine?


Wenn Sie Gleiches und Gleiches vergleichen, sollten Sie die Frage als Unterschied zwischen der VM und der CLR (Common Language Runtime) umformulieren, die der direkte Analogon zur VM ist.
Cletus

Antworten:


278

Es gibt viele Ähnlichkeiten zwischen beiden Implementierungen (und meiner Meinung nach: Ja, beide sind "virtuelle Maschinen").

Zum einen sind sie beide stapelbasierte VMs, ohne die Vorstellung von "Registern", wie wir es von einer modernen CPU wie x86 oder PowerPC gewohnt sind. Die Auswertung aller Ausdrücke ((1 + 1) / 2) erfolgt durch Verschieben von Operanden auf den "Stapel" und anschließendes Entfernen dieser Operanden vom Stapel, wenn ein Befehl (Hinzufügen, Teilen usw.) diese Operanden verbrauchen muss. Jeder Befehl schiebt seine Ergebnisse zurück auf den Stapel.

Dies ist eine bequeme Möglichkeit, eine virtuelle Maschine zu implementieren, da so gut wie jede CPU auf der Welt über einen Stapel verfügt, die Anzahl der Register jedoch häufig unterschiedlich ist (und einige Register für spezielle Zwecke bestimmt sind und jeder Befehl seine Operanden in unterschiedlichen Registern usw. Erwartet ).

Wenn Sie also eine abstrakte Maschine modellieren möchten, ist ein rein stapelbasiertes Modell ein guter Weg.

Natürlich funktionieren echte Maschinen nicht so. Der JIT-Compiler ist also dafür verantwortlich, die "Registrierung" von Bytecode-Operationen durchzuführen und die tatsächlichen CPU-Register so zu planen, dass sie nach Möglichkeit Operanden und Ergebnisse enthalten.

Ich denke, das ist eine der größten Gemeinsamkeiten zwischen der CLR und der JVM.

Was Unterschiede betrifft ...

Ein interessanter Unterschied zwischen den beiden Implementierungen besteht darin, dass die CLR Anweisungen zum Erstellen generischer Typen und zum Anwenden parametrischer Spezialisierungen auf diese Typen enthält. Zur Laufzeit betrachtet die CLR eine Liste <int> als einen völlig anderen Typ als eine Liste <String>.

Unter dem Deckmantel wird für alle Spezialisierungen von Referenztypen dieselbe MSIL verwendet (daher verwendet eine Liste <String> dieselbe Implementierung wie eine Liste <Objekt> mit unterschiedlichen Typumwandlungen an den API-Grenzen), aber jeder Werttyp verwendet eine eigene eindeutige Implementierung (List <int> generiert völlig anderen Code als List <double>).

In Java sind generische Typen ein reiner Compilertrick. Die JVM hat keine Ahnung, welche Klassen Typargumente haben, und kann zur Laufzeit keine parametrischen Spezialisierungen durchführen.

Aus praktischer Sicht bedeutet dies, dass Sie Java-Methoden für generische Typen nicht überladen können. Sie können nicht zwei verschiedene Methoden mit demselben Namen verwenden, die sich nur darin unterscheiden, ob sie eine Liste <String> oder eine Liste <Datum> akzeptieren. Da die CLR über parametrische Typen Bescheid weiß, gibt es natürlich keine Probleme bei der Behandlung von Methoden, die bei generischen Typenspezialisierungen überladen sind.

Das ist der Unterschied, den ich täglich am meisten zwischen der CLR und der JVM bemerke.

Weitere wichtige Unterschiede sind:

  • Die CLR verfügt über Schließungen (implementiert als C # -Delegierte). Die JVM unterstützt Schließungen erst seit Java 8.

  • Die CLR verfügt über Coroutinen (implementiert mit dem Schlüsselwort C # 'yield'). Die JVM nicht.

  • Mit der CLR kann der Benutzercode neue Werttypen (Strukturen) definieren, während die JVM eine feste Sammlung von Werttypen (Byte, Short, Int, Long, Float, Double, Char, Boolean) bereitstellt und nur neue Referenztypen definieren kann. Typen (Klassen).

  • Die CLR bietet Unterstützung für das Deklarieren und Bearbeiten von Zeigern. Dies ist besonders interessant, da sowohl die JVM als auch die CLR strenge Speicher-Collector-Implementierungen zur Komprimierung von Generationen als Speicherverwaltungsstrategie verwenden. Unter normalen Umständen hat ein strikter Komprimierungs-GC Schwierigkeiten mit Zeigern, da beim Verschieben eines Werts von einem Speicherort zu einem anderen alle Zeiger (und Zeiger auf Zeiger) ungültig werden. Die CLR bietet jedoch einen "Pinning" -Mechanismus, mit dem Entwickler einen Codeblock deklarieren können, in dem die CLR bestimmte Zeiger nicht verschieben darf. Es ist sehr praktisch.

  • Die größte Codeeinheit in der JVM ist entweder ein 'Paket', wie durch das Schlüsselwort 'protected' belegt, oder eine JAR (dh Java ARchive), wie durch die Möglichkeit angegeben, ein JAR im Klassenpfad anzugeben und es wie einen Ordner behandeln zu lassen von Code. In der CLR werden Klassen zu 'Assemblys' zusammengefasst, und die CLR bietet Logik zum Überlegen und Bearbeiten von Assemblys (die in "AppDomains" geladen werden und Sandboxen auf Subanwendungsebene für die Speicherzuweisung und Codeausführung bereitstellen).

  • Das CLR-Bytecode-Format (bestehend aus MSIL-Anweisungen und Metadaten) hat weniger Befehlstypen als die JVM. In der JVM hat jede eindeutige Operation (zwei int-Werte hinzufügen, zwei float-Werte hinzufügen usw.) eine eigene eindeutige Anweisung. In der CLR sind alle MSIL-Anweisungen polymorph (fügen Sie zwei Werte hinzu), und der JIT-Compiler ist dafür verantwortlich, die Typen der Operanden zu bestimmen und den entsprechenden Maschinencode zu erstellen. Ich weiß jedoch nicht, welche Strategie am besten geeignet ist. Beide haben Kompromisse. Der HotSpot JIT-Compiler für die JVM kann einen einfacheren Mechanismus zur Codegenerierung verwenden (er muss keine Operandentypen bestimmen, da diese bereits in der Anweisung codiert sind). Dies bedeutet jedoch, dass ein komplexeres Bytecode-Format erforderlich ist. mit mehr Anweisungstypen.

Ich benutze Java (und bewundere die JVM) seit ungefähr zehn Jahren.

Aber meiner Meinung nach ist die CLR jetzt in fast jeder Hinsicht die überlegene Implementierung.


73
Abschlüsse und Generatoren werden auf Sprachebene implementiert und einfach als Klassen auf der CLR-Ebene dargestellt.
Curt Hagenlocher

2
Was ist mit den Unterschieden im Umgang mit dem Haufen? Die CLR ist stärker vom Betriebssystem / Host-Prozess abhängig, während die JVM den Heap-Speicher mehr oder weniger vollständig verwaltet.
Kelly S. French

6
Ein wichtiger Unterschied ist der Kontrast zwischen Just-in-Time-Kompilierung (CLR) und adaptiver Optimierung in (Oracle / Sun) JVM.
Edwin Dalorzo

1
Die lokalen Variablen-Slots von Java verhalten sich ähnlich wie Register. Aber es ist sowieso alles umstritten, da JIT lokale Slots und den Stack in echte Register verwandelt.
Antimon

1
@ kuhajeyan Das liegt daran, dass JVM bei der Einführung von CLR 10 Jahre alt war. Das ist eine lange Zeit in der IT. Als JVM 1993 kam, gab es keinen ernsthaften Konkurrenten, für CLR (2003) gab es eine ausgereifte und solide JVM mit starkem Stand in der Industrie.
Simple Fellow

25

Ihre erste Frage ist der Vergleich der JVM mit .NET Framework. Ich gehe davon aus, dass Sie eigentlich mit der CLR vergleichen wollten. Wenn ja, denke ich, könnten Sie ein kleines Buch darüber schreiben ( BEARBEITEN: sieht so aus, als hätte Benji es bereits :-)

Ein wichtiger Unterschied besteht darin, dass die CLR im Gegensatz zur JVM als sprachneutrale Architektur konzipiert ist.

Ein weiterer wichtiger Unterschied besteht darin, dass die CLR speziell entwickelt wurde, um ein hohes Maß an Interoperabilität mit nativem Code zu ermöglichen. Dies bedeutet, dass die CLR die Zuverlässigkeit und Sicherheit verwalten muss, wenn auf nativen Speicher zugegriffen und dieser geändert wird, sowie das Marshalling zwischen CLR-basierten Datenstrukturen und nativen Datenstrukturen verwalten muss.

Um Ihre zweite Frage zu beantworten, ist der Begriff „virtuelle Maschine“ ein älterer Begriff aus der Hardware-Welt (z. B. IBMs Virtualisierung des 360 in den 1960er Jahren), der früher eine Software- / Hardware-Emulation der zugrunde liegenden Maschine bedeutete, um dieselbe Art von zu erreichen Sachen, die VMWare macht.

Die CLR wird häufig als "Ausführungs-Engine" bezeichnet. In diesem Zusammenhang ist dies eine Implementierung einer IL-Maschine auf einem x86. Dies ist auch das, was die JVM tut, obwohl Sie argumentieren können, dass es einen wichtigen Unterschied zwischen den polymorphen Bytecodes der CLR und den typisierten Bytecodes der JVM gibt.

Die pedantische Antwort auf Ihre zweite Frage lautet also "Nein". Aber es kommt wirklich darauf an, wie Sie diese beiden Begriffe definieren.

BEARBEITEN: Ein weiterer Unterschied zwischen der JVM und der CLR besteht darin, dass die JVM (Version 6) den zugewiesenen Speicher nur ungern an das Betriebssystem zurückgibt, selbst wenn dies möglich ist.

Angenommen, ein JVM-Prozess wird gestartet und weist dem Betriebssystem zunächst 25 MB Speicher zu. Der App-Code versucht dann Zuordnungen, die zusätzliche 50 MB erfordern. Die JVM weist dem Betriebssystem zusätzliche 50 MB zu. Sobald der Anwendungscode diesen Speicher nicht mehr verwendet, wird er durch Müll gesammelt und die Größe des JVM-Heapspeichers nimmt ab. Die JVM gibt jedoch den zugewiesenen Betriebssystemspeicher nur unter bestimmten Umständen frei . Andernfalls bleibt dieser Speicher für den Rest der Prozesslebensdauer zugewiesen.

Die CLR hingegen gibt den zugewiesenen Speicher an das Betriebssystem zurück, wenn er nicht mehr benötigt wird. Im obigen Beispiel hätte die CLR den Speicher freigegeben, sobald der Heap abgenommen hätte.


2
Es ist absolut nicht korrekt, dass die JVM den zugewiesenen Speicher nicht freigibt. Siehe meine Antwort auf diese Frage zum Beweis: stackoverflow.com/questions/366658/…
Michael Borgwardt

Ich habe gesehen, dass die JVM den Speicher wieder an Windows zurückgibt.
Steve Kuo

Ich habe meine Antwort dahingehend geändert, dass die JVM 6 nur sehr ungern Speicher freigibt, mit Links zu den Antworten von Ran und Michael. Ich habe dieses Verhalten bei JVM 5 nie gesehen, daher war diese Version möglicherweise noch widerstrebender.
HTTP 410

Können Sie erläutern, wie die JVM den Heap aktiv verwaltet, während sich die CLR auf den übergeordneten Prozess stützt? Das spezifische Beispiel, das ich verwende, ist, dass die JVM Laufzeitargumente für die maximale Heap-Größe hat, während die Standard-CLR-Umgebung dies nicht tut. Zwar kann eine unter IIS gehostete CLR-App IIS so konfigurieren, dass der Speicher begrenzt wird, dies würde jedoch bedeuten, dass IIS in die Definition der virtuellen Maschine einbezogen wird.
Kelly S. French

@ Steve Kuo, ja das habe ich auch gesehen. normalerweise zwischen 17 und 18 Uhr.
Simple Fellow

11

Die CLR und die JVM sind beide virtuelle Maschinen.

Das .NET Framework und die Java Runtime Environment sind die Bündelung der jeweiligen VMs und ihrer Bibliotheken. Ohne Bibliotheken sind die VMs ziemlich nutzlos.


11

Weitere Einzelheiten zu den Unterschieden finden Sie in verschiedenen akademischen und privaten Quellen. Ein gutes Beispiel ist CLR Design Choices .

Einige spezifische Beispiele sind:

  • Einige Low-Level-Opperanden sind typisiert, z. B. "Add Two Ints", wobei CLR einen polymorphen Operanden verwendet. (dh fadd / iadd / ladd vs nur hinzufügen)
  • Derzeit führt die JVM eine aggressivere Laufzeitprofilerstellung und -optimierung durch (z. B. Hotspot). CLR führt derzeit JIT-Optimierungen durch, jedoch keine Laufzeitoptimierung (dh Code während der Ausführung ersetzen).
  • CLR integriert keine virtuellen Methoden, JVM ...
  • Unterstützung für Werttypen in der CLR, die über die "Grundelemente" hinausgehen.

-11

Es ist keine virtuelle Maschine, das .net-Framework kompiliert die Assemblys zum Zeitpunkt des ersten Durchlaufs in native Binärdateien:

Beim Rechnen ist die Just-in-Time-Kompilierung (JIT), auch als dynamische Übersetzung bekannt, eine Technik zur Verbesserung der Laufzeitleistung eines Computerprogramms. JIT baut auf zwei früheren Ideen in Laufzeitumgebungen auf: Bytecode-Kompilierung und dynamische Kompilierung. Es konvertiert Code zur Laufzeit, bevor er nativ ausgeführt wird, z. B. Bytecode, in nativen Maschinencode. Die Leistungsverbesserung gegenüber Interpreten resultiert aus dem Zwischenspeichern der Ergebnisse der Übersetzung von Codeblöcken und nicht einfach aus der Neubewertung jeder Zeile oder jedes Operanden bei jeder Erfüllung (siehe Interpretierte Sprache). Es hat auch Vorteile gegenüber dem statischen Kompilieren des Codes zur Entwicklungszeit, da es den Code neu kompilieren kann, wenn sich dies als vorteilhaft herausstellt, und möglicherweise Sicherheitsgarantien durchsetzen kann.

Einige moderne Laufzeitumgebungen wie Microsoft .NET Framework, die meisten Implementierungen von Java und zuletzt Actionscript 3 basieren auf der JIT-Kompilierung für die schnelle Codeausführung.

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

Das Hinzufügen von .NET Framework enthält genau wie Java eine virtuelle Maschine.


10
Nur weil die virtuelle Maschine JIT zur Leistungsoptimierung verwendet, heißt das nicht, dass es sich nicht mehr um eine virtuelle Maschine handelt. Wenn der Programmierer kompiliert, kompiliert er auf die virtuelle Maschine und überlässt es der Implementierung, die Ausführung nach
eigenem Ermessen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.