Máquina virtual de Java y CLR

140

¿Como una especie de seguimiento de la pregunta llamada Diferencias entre MSIL y el código de bytes de Java? , ¿cuáles son las (principales) diferencias o similitudes en la forma en que funciona la máquina virtual Java versus cómo.NET Framework Common Language Runtime (CLR) funciona?

Además, es el .NET Framework ¿CLR una "máquina virtual" o no tiene los atributos de una máquina virtual?

Frank V
fuente
Bueno, si está comparando like and like, debería reformular la pregunta como la diferencia entre la VM y el CLR (Common Language Runtime), que es el análogo directo a la VM.
cletus

Respuestas:

278

Hay muchas similitudes entre ambas implementaciones (y en mi opinión: sí, ambas son "máquinas virtuales").

Por un lado, ambas son máquinas virtuales basadas en pila, sin la noción de "registros" como estamos acostumbrados a ver en una CPU moderna como la x86 o PowerPC. La evaluación de todas las expresiones ((1 + 1) / 2) se realiza insertando operandos en la "pila" y luego sacando esos operandos de la pila cada vez que una instrucción (agregar, dividir, etc.) necesita consumir esos operandos. Cada instrucción devuelve sus resultados a la pila.

Es una forma conveniente de implementar una máquina virtual, porque casi todas las CPU del mundo tienen una pila, pero la cantidad de registros a menudo es diferente (y algunos registros tienen un propósito especial, y cada instrucción espera sus operandos en diferentes registros, etc. )

Entonces, si va a modelar una máquina abstracta, un modelo puramente basado en pila es una muy buena manera de hacerlo.

Por supuesto, las máquinas reales no funcionan de esa manera. Por lo tanto, el compilador JIT es responsable de realizar el "registro" de las operaciones de código de bytes, esencialmente programando los registros reales de la CPU para contener operandos y resultados siempre que sea posible.

Entonces, creo que ese es uno de los mayores puntos en común entre CLR y JVM.

En cuanto a las diferencias ...

Una diferencia interesante entre las dos implementaciones es que el CLR incluye instrucciones para crear tipos genéricos y luego para aplicar especializaciones paramétricas a esos tipos. Entonces, en tiempo de ejecución, el CLR considera que una Lista <int> es un tipo completamente diferente de una Lista <String>.

Debajo de las cubiertas, usa el mismo MSIL para todas las especializaciones de tipo de referencia (por lo que una Lista <String> usa la misma implementación que una Lista <Objeto>, con diferentes tipos de conversión en los límites de la API), pero cada tipo de valor usa su propia implementación única (List <int> genera un código completamente diferente de List <double>).

En Java, los tipos genéricos son un truco puramente de compilación. La JVM no tiene idea de qué clases tienen argumentos de tipo, y no puede realizar especializaciones paramétricas en tiempo de ejecución.

Desde una perspectiva práctica, eso significa que no puede sobrecargar los métodos Java en tipos genéricos. No puede tener dos métodos diferentes, con el mismo nombre, que difieren solo en si aceptan una Lista <String> o una Lista <Fecha>. Por supuesto, dado que el CLR conoce los tipos paramétricos, no tiene problemas para manejar métodos sobrecargados en especializaciones de tipos genéricos.

En el día a día, esa es la diferencia que más noto entre el CLR y la JVM.

Otras diferencias importantes incluyen:

  • El CLR tiene cierres (implementados como delegados de C #). La JVM solo admite cierres desde Java 8.

  • El CLR tiene corutinas (implementadas con la palabra clave 'rendimiento' C #). La JVM no lo hace.

  • El CLR permite que el código de usuario defina nuevos tipos de valores (estructuras), mientras que la JVM proporciona una colección fija de tipos de valores (byte, short, int, long, float, double, char, boolean) y solo permite a los usuarios definir nuevas referencias. tipos (clases).

  • El CLR proporciona soporte para declarar y manipular punteros. Esto es especialmente interesante porque tanto la JVM como la CLR emplean implementaciones estrictas de recolección de basura de compactación generacional como su estrategia de administración de memoria. En circunstancias normales, un GC de compactación estricto tiene dificultades con los punteros, porque cuando mueve un valor de una ubicación de memoria a otra, todos los punteros (y punteros a punteros) se vuelven inválidos. Pero el CLR proporciona un mecanismo de "fijación" para que los desarrolladores puedan declarar un bloque de código dentro del cual el CLR no puede mover ciertos punteros. Es muy conveniente.

  • La unidad de código más grande en la JVM es un 'paquete' como lo demuestra la palabra clave 'protegida' o posiblemente un JAR (es decir, Java ARchive) como puede ser capaz de especificar un jar en el classpath y tratarlo como una carpeta de código En el CLR, las clases se agregan en 'ensamblajes', y el CLR proporciona lógica para razonar y manipular ensamblajes (que se cargan en "AppDomains", proporcionando entornos limitados a nivel de sub-aplicación para la asignación de memoria y la ejecución de código).

  • El formato de código de bytes CLR (compuesto por instrucciones y metadatos MSIL) tiene menos tipos de instrucciones que la JVM. En la JVM, cada operación única (agregar dos valores int, agregar dos valores flotantes, etc.) tiene su propia instrucción única. En el CLR, todas las instrucciones de MSIL son polimórficas (agregue dos valores) y el compilador JIT es responsable de determinar los tipos de operandos y crear el código de máquina apropiado. Sin embargo, no sé cuál es la estrategia preferible. Ambos tienen compensaciones. El compilador JIT de HotSpot, para la JVM, puede usar un mecanismo de generación de código más simple (no necesita determinar los tipos de operandos, porque ya están codificados en la instrucción), pero eso significa que necesita un formato de código de bytes más complejo, con más tipos de instrucción

He estado usando Java (y admirando la JVM) durante unos diez años.

Pero, en mi opinión, el CLR es ahora la implementación superior, en casi todos los sentidos.

benjismith
fuente
73
Los cierres y generadores se implementan a nivel de lenguaje y simplemente se representan como clases en el nivel CLR.
Curt Hagenlocher
2
¿Qué pasa con las diferencias en cómo manejan el montón? El CLR depende más del proceso del sistema operativo / host, mientras que la JVM gestiona la memoria del montón más o menos por completo.
Kelly S. French
66
Una diferencia importante es el contraste entre la compilación justo a tiempo (CLR) y la optimización adaptativa en (Oracle / Sun) JVM.
Edwin Dalorzo
1
Las ranuras de variables locales de Java actúan como registros. Pero todo es discutible de todos modos ya que JIT convierte las ranuras locales y la pila en registros reales.
Antimonio
1
@kuhajeyan es porque cuando se introdujo CLR, JVM tenía 10 años. eso es mucho tiempo en TI. Cuando JVM llegó en 1993 no había un contendiente serio, para CLR (2003) había una JVM madura y sólida con un fuerte punto de apoyo en la industria.
Simple Fellow
25

Su primera pregunta es comparar JVM con .NET Framework. Supongo que en realidad tenía la intención de comparar con CLR. Si es así, creo que podrías escribir un pequeño libro sobre esto ( EDITAR: parece que Benji ya tiene :-)

Una diferencia importante es que el CLR está diseñado para ser una arquitectura de lenguaje neutral, a diferencia de la JVM.

Otra diferencia importante es que el CLR fue diseñado específicamente para permitir un alto nivel de interoperabilidad con código nativo. Esto significa que el CLR debe administrar la confiabilidad y la seguridad cuando se accede y modifica la memoria nativa, y también debe administrar la clasificación entre las estructuras de datos basadas en CLR y las estructuras de datos nativas.

Para responder a su segunda pregunta, el término "máquina virtual" es un término antiguo del mundo del hardware (por ejemplo, la virtualización de 360 ​​de IBM en la década de 1960) que solía significar una emulación de software / hardware de la máquina subyacente para lograr el mismo tipo de cosas que hace VMWare.

El CLR a menudo se conoce como un "motor de ejecución". En este contexto, es una implementación de una máquina IL encima de un x86. Esto también es lo que hace la JVM, aunque puede argumentar que hay una diferencia importante entre los códigos de bytes polimórficos de CLR y los códigos de bytes escritos de JVM.

Entonces la respuesta pedante a su segunda pregunta es "no". Pero realmente se trata de cómo define estos dos términos.

EDITAR: Una diferencia más entre la JVM y la CLR es que la JVM (versión 6) es muy reacia a devolver la memoria asignada al sistema operativo, incluso donde puede.

Por ejemplo, supongamos que un proceso JVM se inicia y asigna inicialmente 25 MB de memoria del sistema operativo. Luego, el código de la aplicación intenta asignaciones que requieren 50 MB adicionales. La JVM asignará 50 MB adicionales del sistema operativo. Una vez que el código de la aplicación ha dejado de usar esa memoria, se recolecta basura y el tamaño del montón JVM disminuirá. Sin embargo, la JVM solo liberará la memoria asignada del sistema operativo bajo ciertas circunstancias muy específicas . De lo contrario, durante el resto de la vida útil del proceso, esa memoria permanecerá asignada.

El CLR, por otro lado, libera la memoria asignada al sistema operativo si ya no es necesaria. En el ejemplo anterior, el CLR habría liberado la memoria una vez que el montón hubiera disminuido.

HTTP 410
fuente
2
Absolutamente no es correcto que la JVM no libere memoria asignada. Vea mi respuesta a esta pregunta como prueba: stackoverflow.com/questions/366658/…
Michael Borgwardt
He visto a la JVM devolver la memoria a Windows.
Steve Kuo el
Cambié mi respuesta para decir que el JVM 6 es muy reacio a liberar memoria, con enlaces a las respuestas de Ran y Michael. Nunca vi este comportamiento con JVM 5, por lo que tal vez esa versión era aún más reacia.
HTTP 410 el
¿Podría explicar cómo la JVM gestiona activamente el montón mientras el CLR se basa en el proceso principal? El ejemplo específico que uso es que la JVM tiene argumentos de tiempo de ejecución para el tamaño máximo de almacenamiento dinámico, mientras que el entorno CLR predeterminado no. Si bien es cierto que una aplicación CLR alojada bajo IIS puede configurar IIS para limitar la memoria, eso significaría incluir IIS en la definición de máquina virtual.
Kelly S. French
@ Steve Kuo, sí, yo también lo he visto. generalmente entre las 5 p.m. y las 6 p.m.
Simple Fellow
11

El CLR y la JVM son máquinas virtuales.

.NET Framework y Java Runtime Environment son la agrupación de las respectivas máquinas virtuales y sus bibliotecas. Sin bibliotecas, las máquinas virtuales son bastante inútiles.

Allain Lalonde
fuente
11

Se pueden encontrar más detalles sobre las diferencias en varias fuentes académicas y privadas. Una vez que un buen ejemplo es CLR Design Choices .

Algunos ejemplos específicos incluyen:

  • Algunos operandos de bajo nivel se escriben como "agregar dos entradas" donde CLR usa un operando polimórfico. (es decir, fadd / iadd / ladd versus solo agregar)
  • Actualmente, la JVM realiza perfiles de optimización y optimización de tiempo de ejecución más agresivos (es decir, Hotspot). CLR actualmente realiza optimizaciones JIT, pero no optimiza el tiempo de ejecución (es decir, reemplaza el código mientras se está ejecutando).
  • CLR no integra métodos virtuales, JVM sí ...
  • Soporte para tipos de valores en el CLR más allá de las "primitivas".
James Schek
fuente
-11

No es una máquina virtual, el marco .net compila los ensamblajes en binario nativo en el momento de la primera ejecución:

En informática, la compilación justo a tiempo (JIT), también conocida como traducción dinámica, es una técnica para mejorar el rendimiento en tiempo de ejecución de un programa de computadora. JIT se basa en dos ideas anteriores en entornos de tiempo de ejecución: compilación de bytecode y compilación dinámica. Convierte el código en tiempo de ejecución antes de ejecutarlo de forma nativa, por ejemplo, el código de bytes en código máquina nativo. La mejora del rendimiento sobre los intérpretes se origina en el almacenamiento en caché de los resultados de la traducción de bloques de código, y no simplemente en la reevaluación de cada línea u operando cada vez que se cumple (ver Lenguaje interpretado). También tiene ventajas sobre la compilación estática del código en el momento del desarrollo, ya que puede recompilar el código si se considera ventajoso y puede hacer cumplir las garantías de seguridad.

Varios entornos de tiempo de ejecución modernos, como .NET Framework de Microsoft, la mayoría de las implementaciones de Java y, más recientemente, Actionscript 3, se basan en la compilación JIT para la ejecución de código de alta velocidad.

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

Agregar .NET Framework contiene una máquina virtual, como Java.

Diones
fuente
10
El hecho de que la máquina virtual utilice JIT para la optimización del rendimiento no significa que ya no sea una máquina virtual. Cuando el programador compila, compila en la máquina virtual, dejando la implementación para realizar la ejecución como lo considere conveniente
Allain Lalonde