Macchina virtuale e CLR di Java


140

Come una sorta di seguito alla domanda chiamata Differenze tra il codice byte MSIL e Java? , quali sono le (maggiori) differenze o somiglianze nel modo in cui la Java Virtual Machine funziona rispetto a come.NET Framework Common Language Runtime (CLR) funziona?

Inoltre, è il .NET framework CLR una "macchina virtuale" o non ha gli attributi di una macchina virtuale?


Bene, se stai confrontando like e like, dovresti riformulare la domanda come differenza tra la VM e il CLR (Common Language Runtime), che è l'analogo diretto alla VM.
cletus,

Risposte:


278

Ci sono molte somiglianze tra entrambe le implementazioni (e secondo me: sì, sono entrambe "macchine virtuali").

Per prima cosa, sono entrambe macchine virtuali basate su stack, senza la nozione di "registri" come siamo abituati a vedere in una CPU moderna come x86 o PowerPC. La valutazione di tutte le espressioni ((1 + 1) / 2) viene eseguita spingendo gli operandi sullo "stack" e quindi estraendo quegli operandi dallo stack ogni volta che un'istruzione (add, divide, ecc.) Deve consumare quegli operandi. Ogni istruzione rimanda i suoi risultati allo stack.

È un modo conveniente per implementare una macchina virtuale, perché praticamente tutte le CPU del mondo hanno uno stack, ma il numero di registri è spesso diverso (e alcuni registri sono specifici, e ogni istruzione prevede i suoi operandi in registri diversi, ecc. ).

Quindi, se hai intenzione di modellare una macchina astratta, un modello puramente basato su stack è un buon modo per andare.

Naturalmente, le macchine reali non funzionano in questo modo. Quindi il compilatore JIT è responsabile dell'esecuzione della "registrazione" delle operazioni bytecode, essenzialmente programmando i registri effettivi della CPU per contenere operandi e risultati ogni volta che è possibile.

Quindi, penso che sia uno dei maggiori punti in comune tra CLR e JVM.

Per quanto riguarda le differenze ...

Una differenza interessante tra le due implementazioni è che il CLR include istruzioni per la creazione di tipi generici e quindi per l'applicazione di specializzazioni parametriche a tali tipi. Quindi, in fase di esecuzione, il CLR considera un Elenco <int> come un tipo completamente diverso da un Elenco <String>.

Sotto le copertine, utilizza lo stesso MSIL per tutte le specializzazioni del tipo di riferimento (quindi un Elenco <String> utilizza la stessa implementazione di un Elenco <Oggetto>, con cast di tipi diversi ai limiti dell'API), ma ogni tipo di valore utilizza la sua implementazione unica (Elenco <int> genera codice completamente diverso dall'elenco <doppio>).

In Java, i tipi generici sono puramente un trucco da compilatore. JVM non ha idea di quali classi abbiano argomenti di tipo e non è in grado di eseguire specializzazioni parametriche in fase di esecuzione.

Da un punto di vista pratico, ciò significa che non è possibile sovraccaricare i metodi Java su tipi generici. Non puoi avere due metodi diversi, con lo stesso nome, differenti solo se accettano un Elenco <String> o un Elenco <Data>. Naturalmente, poiché il CLR conosce i tipi parametrici, non ha problemi a gestire i metodi sovraccarichi di specializzazioni di tipo generico.

Su base giornaliera, questa è la differenza che noto di più tra CLR e JVM.

Altre differenze importanti includono:

  • Il CLR ha chiusure (implementate come delegati C #). JVM supporta le chiusure solo da Java 8.

  • Il CLR ha coroutine (implementato con la parola chiave "rendimento" C #). La JVM no.

  • Il CLR consente al codice utente di definire nuovi tipi di valore (strutture), mentre la JVM fornisce una raccolta fissa di tipi di valore (byte, short, int, long, float, double, char, boolean) e consente solo agli utenti di definire nuovi riferimenti- tipi (classi).

  • Il CLR fornisce supporto per la dichiarazione e la manipolazione di puntatori. Ciò è particolarmente interessante perché sia ​​la JVM che il CLR utilizzano implementazioni rigorose di compattazione di generatori di rifiuti come strategia di gestione della memoria. In circostanze normali, un GC di compattazione rigorosa ha davvero dei problemi con i puntatori, perché quando si sposta un valore da una posizione di memoria a un'altra, tutti i puntatori (e puntatori a puntatori) diventano non validi. Ma il CLR fornisce un meccanismo di "blocco" in modo che gli sviluppatori possano dichiarare un blocco di codice all'interno del quale al CLR non è consentito spostare determinati puntatori. È molto conveniente.

  • La più grande unità di codice nella JVM è un "pacchetto" come evidenziato dalla parola chiave "protetta" o probabilmente un JAR (ovvero Java ARchive) come evidenziato dalla capacità di specificare un vaso nel percorso di classe e farlo trattare come una cartella di codice. Nel CLR, le classi sono aggregate in "assembly" e il CLR fornisce la logica per ragionare e manipolare gli assembly (che vengono caricati in "AppDomains", fornendo sandbox a livello di applicazione secondaria per l'allocazione della memoria e l'esecuzione del codice).

  • Il formato bytecode CLR (composto da istruzioni MSIL e metadati) ha meno tipi di istruzioni rispetto alla JVM. Nella JVM, ogni operazione unica (aggiungi due valori int, aggiungi due valori float, ecc.) Ha le sue istruzioni uniche. Nel CLR, tutte le istruzioni MSIL sono polimorfiche (aggiungere due valori) e il compilatore JIT è responsabile della determinazione dei tipi di operandi e della creazione del codice macchina appropriato. Tuttavia, non so quale sia la strategia preferibile. Entrambi hanno dei compromessi. Il compilatore JIT HotSpot, per JVM, può utilizzare un meccanismo di generazione del codice più semplice (non è necessario determinare i tipi di operando, poiché sono già codificati nell'istruzione), ma ciò significa che richiede un formato bytecode più complesso, con più tipi di istruzioni.

Sto usando Java (e ammirando la JVM) da circa dieci anni.

Ma, secondo me, il CLR è ora l'implementazione superiore, in quasi tutti i modi.


73
Le chiusure e i generatori sono implementati a livello linguistico e sono semplicemente rappresentati come classi a livello CLR.
Curt Hagenlocher,

2
E le differenze nel modo in cui gestiscono l'heap? Il CLR dipende maggiormente dal proc OS / host mentre la JVM gestisce la memoria dell'heap più o meno completamente.
Kelly S. francese,

6
Una differenza importante è il contrasto tra compilazione just-in-time (CLR) e ottimizzazione adattiva nella JVM (Oracle / Sun).
Edwin Dalorzo,

1
Gli slot delle variabili locali Java si comportano in modo molto simile ai registri. Ma è comunque tutto discutibile poiché JIT trasforma gli slot locali e lo stack in registri reali.
Antimonio

1
@kuhajeyan è perché quando è stato introdotto CLR, JVM aveva 10 anni. è molto tempo nell'IT. Quando nel 1993 arrivò la JVM non vi furono contendenti seri, per la CLR (2003) c'era una JVM matura e solida con una solida base nell'industria.
Simple Fellow,

25

La tua prima domanda è il confronto tra la JVM e il .NET Framework - suppongo che in realtà tu intendessi confrontare con il CLR. Se è così, penso che potresti scrivere un piccolo libro su questo ( EDIT: sembra che Benji abbia già :-)

Una differenza importante è che CLR è progettato per essere un'architettura indipendente dal linguaggio, a differenza di JVM.

Un'altra differenza importante è che il CLR è stato specificamente progettato per consentire un alto livello di interoperabilità con il codice nativo. Ciò significa che il CLR deve gestire l'affidabilità e la sicurezza quando si accede e modifica la memoria nativa, nonché gestire il marshalling tra strutture di dati basate su CLR e strutture di dati native.

Per rispondere alla tua seconda domanda, il termine "macchina virtuale" è un termine più antico del mondo dell'hardware (ad esempio la virtualizzazione di IBM della 360 negli anni '60) che significava un'emulazione software / hardware della macchina sottostante per ottenere lo stesso tipo di cose che fa VMWare.

Il CLR viene spesso definito "motore di esecuzione". In questo contesto, questa è un'implementazione di una macchina IL sopra un x86. Questo è anche ciò che fa la JVM, sebbene si possa sostenere che esiste una differenza importante tra i bytecode polimorfici del CLR e i bytecode tipizzati della JVM.

Quindi la risposta pedante alla tua seconda domanda è "no". Ma dipende davvero da come definisci questi due termini.

EDIT: Un'altra differenza tra JVM e CLR è che JVM (versione 6) è molto riluttante a rilasciare la memoria allocata al sistema operativo, anche dove è possibile.

Ad esempio, supponiamo che un processo JVM inizi e alloca inizialmente 25 MB di memoria dal sistema operativo. Il codice dell'app tenta quindi allocazioni che richiedono ulteriori 50 MB. La JVM assegnerà altri 50 MB dal sistema operativo. Una volta che il codice dell'applicazione ha smesso di usare quella memoria, viene raccolto in modo inutile e la dimensione dell'heap JVM diminuirà. Tuttavia, JVM libererà la memoria del sistema operativo allocata solo in determinate circostanze molto specifiche . Altrimenti, per il resto della durata del processo quella memoria rimarrà allocata.

Il CLR, d'altra parte, rilascia la memoria allocata al sistema operativo se non è più necessario. Nell'esempio sopra, il CLR avrebbe rilasciato la memoria una volta che l'heap fosse diminuito.


2
È assolutamente errato che la JVM non libererà memoria allocata. Vedi la mia risposta a questa domanda per la prova: stackoverflow.com/questions/366658/…
Michael Borgwardt,

Ho visto la JVM restituire la memoria a Windows.
Steve Kuo,

Ho cambiato la mia risposta per dire che JVM 6 è molto riluttante a liberare memoria, con collegamenti alle risposte di Ran e Michael. Non ho mai visto questo comportamento con JVM 5, quindi forse quella versione era ancora più riluttante.
HTTP 410,

Potresti spiegare come la JVM gestisce attivamente l'heap mentre il CLR si basa sul processo parent? L'esempio specifico che uso è che JVM ha argomenti di runtime per la dimensione heap massima, mentre l'ambiente CLR predefinito no. Mentre è vero che un'app CLR ospitata in IIS può configurare IIS per limitare la memoria, ciò significherebbe includere IIS nella definizione di macchina virtuale.
Kelly S. francese,

@Steve Kuo, sì, l'ho visto anche io. di solito tra le 17:00 e le 18:00.
Simple Fellow,

11

CLR e JVM sono entrambe macchine virtuali.

.NET Framework e Java Runtime Environment sono il raggruppamento delle rispettive VM e delle loro librerie. Senza librerie le VM sono abbastanza inutili.


11

Maggiori dettagli sulle differenze sono disponibili presso varie fonti accademiche e private. Un buon esempio è CLR Design Choices .

Alcuni esempi specifici includono:

  • Alcuni opperand di basso livello sono digitati come "aggiungi due in" dove CLR usa un operando polimorfico. (es. fadd / iadd / ladd vs basta aggiungere)
  • Attualmente, la JVM esegue una profilazione e un'ottimizzazione del runtime più aggressive (ad es. Hotspot). CLR attualmente esegue le ottimizzazioni JIT, ma non l'ottimizzazione del runtime (ovvero sostituisci il codice mentre sei in esecuzione).
  • CLR non incorpora metodi virtuali, JVM fa ...
  • Supporto per tipi di valore nel CLR oltre alle "primitive".

-11

Non è una macchina virtuale, il framework .net compila gli assembly in binario nativo al momento della prima esecuzione:

Nell'informatica, la compilazione just-in-time (JIT), nota anche come traduzione dinamica, è una tecnica per migliorare le prestazioni di runtime di un programma per computer. JIT si basa su due idee precedenti in ambienti di runtime: compilazione bytecode e compilazione dinamica. Converte il codice in fase di esecuzione prima di eseguirlo in modo nativo, ad esempio bytecode in codice macchina nativo. Il miglioramento delle prestazioni rispetto agli interpreti deriva dalla memorizzazione nella cache dei risultati della traduzione di blocchi di codice e non dalla semplice rivalutazione di ogni riga o operando ogni volta che viene soddisfatta (vedere Lingua interpretata). Presenta inoltre vantaggi rispetto alla compilazione statica del codice in fase di sviluppo, in quanto può ricompilare il codice se viene ritenuto vantaggioso e può far valere le garanzie di sicurezza.

Diversi ambienti di runtime moderni, come .NET Framework di Microsoft, la maggior parte delle implementazioni di Java e, più recentemente, Actionscript 3, si basano sulla compilazione JIT per l'esecuzione di codice ad alta velocità.

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

L'aggiunta di .NET framework contiene una macchina virtuale, proprio come Java.


10
Solo perché la macchina virtuale utilizza JIT per l'ottimizzazione delle prestazioni non significa che non sia più una macchina virtuale. Quando il programmatore compila, compila sulla macchina virtuale, lasciando l'implementazione per eseguire l'esecuzione come meglio ritiene opportuno
Allain Lalonde,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.