stringtranslate.com

Máquina virtual de Java

Descripción general de una arquitectura de máquina virtual Java (JVM) basada en la especificación de máquina virtual Java Java SE 7 Edition

Una máquina virtual Java ( JVM ) es una máquina virtual que permite a una computadora ejecutar programas Java , así como programas escritos en otros lenguajes que también están compilados en código de bytes Java . La JVM se detalla mediante una especificación que describe formalmente lo que se requiere en una implementación de JVM. Tener una especificación garantiza la interoperabilidad de los programas Java en diferentes implementaciones, de modo que los autores de programas que utilizan el Java Development Kit (JDK) no tengan que preocuparse por las idiosincrasias de la plataforma de hardware subyacente.

La implementación de referencia JVM es desarrollada por el proyecto OpenJDK como código fuente abierto e incluye un compilador JIT llamado HotSpot . Las versiones de Java con soporte comercial disponibles en Oracle se basan en el tiempo de ejecución OpenJDK. Eclipse OpenJ9 es otra JVM de código abierto para OpenJDK.

especificación JVM

La máquina virtual Java es una computadora abstracta (virtual) definida por una especificación. Es parte del entorno de ejecución de Java. No se especifica el algoritmo de recolección de basura utilizado ni cualquier optimización interna de las instrucciones de la máquina virtual Java (su traducción al código de máquina ). La razón principal de esta omisión es no limitar innecesariamente a los implementadores. Cualquier aplicación Java sólo puede ejecutarse dentro de alguna implementación concreta de la especificación abstracta de la máquina virtual Java. [2]

A partir de Java Platform, Standard Edition (J2SE) 5.0, los cambios en la especificación JVM se desarrollaron bajo el proceso de la comunidad Java como JSR 924. [3] A partir de 2006 , los cambios en la especificación para admitir los cambios propuestos en el formato del archivo de clase ( JSR 202) [4] se están realizando como una versión de mantenimiento de JSR 924. La especificación para JVM se publicó como el libro azul , [5] cuyo prefacio dice:

Pretendemos que esta especificación documente suficientemente la máquina virtual Java para hacer posibles implementaciones compatibles en salas limpias. Oracle proporciona pruebas que verifican el correcto funcionamiento de las implementaciones de la Máquina Virtual Java.

Una de las JVM de Oracle se llama HotSpot; el otro, heredado de BEA Systems , es JRockit . Oracle posee la marca registrada Java y puede permitir su uso para certificar que los conjuntos de implementación son totalmente compatibles con las especificaciones de Oracle.

Cargador de clases

Una de las unidades organizativas del código de bytes JVM es una clase . Una implementación de cargador de clases debe poder reconocer y cargar cualquier cosa que se ajuste al formato de archivo de clase Java . Cualquier implementación es libre de reconocer otras formas binarias además de los archivos de clase , pero debe reconocer los archivos de clase .

El cargador de clases realiza tres actividades básicas en este estricto orden:

  1. Cargando: busca e importa los datos binarios para un tipo
  2. Vinculación: realiza verificación, preparación y (opcionalmente) resolución.
    • Verificación: asegura la exactitud del tipo importado
    • Preparación: asigna memoria para variables de clase e inicializa la memoria a los valores predeterminados
    • Resolución: transforma referencias simbólicas del tipo en referencias directas.
  3. Inicialización: invoca código Java que inicializa las variables de clase a sus valores iniciales adecuados.

En general, existen tres tipos de cargador de clases: cargador de clases de arranque, cargador de clases de extensión y cargador de clases de Sistema/Aplicación.

Cada implementación de máquina virtual Java debe tener un cargador de clases de arranque que sea capaz de cargar clases confiables, así como un cargador de clases de extensión o un cargador de clases de aplicaciones. La especificación de la máquina virtual Java no especifica cómo un cargador de clases debe ubicar las clases.

Arquitectura de máquina virtual

La JVM opera con tipos específicos de datos como se especifica en las especificaciones de la máquina virtual Java. Los tipos de datos se pueden dividir [6] en tipos primitivos ( enteros , de punto flotante, largos, etc.) y tipos de referencia. Las JVM anteriores eran sólo máquinas de 32 bits . longy doublelos tipos, que son de 64 bits , son compatibles de forma nativa, pero consumen dos unidades de almacenamiento en las variables locales o en la pila de operandos de un marco, ya que cada unidad es de 32 bits. boolean, byte, shorty charlos tipos tienen extensión de signo (excepto charcuál tiene extensión de cero ) y funcionan como enteros de 32 bits, al igual que intlos tipos. Los tipos más pequeños solo tienen unas pocas instrucciones específicas para cargar, almacenar y convertir tipos. booleanse opera como bytevalores de 8 bits, donde 0 representa falsey 1 representa true. (Aunque booleanse ha tratado como un tipo desde que la especificación de la máquina virtual Java, segunda edición, aclaró este problema, en el código compilado y ejecutado hay poca diferencia entre a booleany a, byteexcepto por la alteración de nombres en las firmas de métodos y el tipo de matrices booleanas. booleans en las firmas de los métodos están destrozadas Zmientras que bytelos s están destrozados como B. Las matrices booleanas llevan el tipo boolean[]pero usan 8 bits por elemento, y la JVM no tiene capacidad incorporada para empaquetar valores booleanos en una matriz de bits , por lo que, excepto por el tipo que realizan y se comportan, igual que bytelas matrices. En todos los demás usos, el booleantipo es efectivamente desconocido para la JVM ya que todas las instrucciones para operar en booleanos también se usan para operar en bytes.) Sin embargo, las versiones más nuevas de JVM (OpenJDK HotSpot JVM) admiten 64 bits, por lo que Puede tener JVM de 32 o 64 bits en un sistema operativo de 64 bits. La principal ventaja de ejecutar Java en un entorno de 64 bits es el mayor espacio de direcciones. Esto permite un tamaño de montón de Java mucho mayor y un mayor número máximo de subprocesos de Java, que es necesario para ciertos tipos de aplicaciones grandes; sin embargo, el rendimiento se ve afectado al utilizar JVM de 64 bits en comparación con JVM de 32 bits.

La JVM tiene un montón de basura para almacenar objetos y matrices. El código, las constantes y otros datos de clase se almacenan en el "área de método". El área del método es lógicamente parte del montón, pero las implementaciones pueden tratar el área del método por separado del montón y, por ejemplo, es posible que no la recolecten como basura. Cada subproceso JVM también tiene su propia pila de llamadas (llamada "pila de máquina virtual Java" para mayor claridad), que almacena marcos . Se crea un nuevo marco cada vez que se llama a un método y el marco se destruye cuando ese método sale.

Cada cuadro proporciona una "pila de operandos" y una matriz de "variables locales". La pila de operandos se usa para que los operandos ejecuten cálculos y reciban el valor de retorno de un método llamado, mientras que las variables locales tienen el mismo propósito que los registros y también se usan para pasar argumentos del método. Por tanto, la JVM es a la vez una máquina de pila y una máquina de registro . En la práctica, HotSpot elimina por completo todas las pilas además del hilo nativo/pila de llamadas incluso cuando se ejecuta en modo interpretado, ya que su Templating Interpreter funciona técnicamente como un compilador.

Instrucciones de código de bytes

La JVM tiene instrucciones para los siguientes grupos de tareas:

El objetivo es la compatibilidad binaria. Cada sistema operativo host en particular necesita su propia implementación de JVM y tiempo de ejecución. Estas JVM interpretan el código de bytes semánticamente de la misma manera, pero la implementación real puede ser diferente. Más complejo que simplemente emular código de bytes es implementar de manera compatible y eficiente la API central de Java que debe asignarse a cada sistema operativo host.

Estas instrucciones operan sobre un conjunto detipos de datos abstractos en lugar de los tipos de datos nativos de cualquier arquitectura de conjunto de instrucciones específica .

lenguajes JVM

Un lenguaje JVM es cualquier lenguaje con funcionalidad que pueda expresarse en términos de un archivo de clase válido que pueda alojarse en la máquina virtual Java. Un archivo de clase contiene instrucciones de la máquina virtual Java ( código de bytes de Java ) y una tabla de símbolos, así como otra información auxiliar. El formato de archivo de clase es el formato binario independiente del hardware y del sistema operativo que se utiliza para representar clases e interfaces compiladas. [7]

Hay varios lenguajes JVM, tanto lenguajes antiguos portados a JVM como lenguajes completamente nuevos. JRuby y Jython son quizás los puertos más conocidos de los lenguajes existentes, es decir, Ruby y Python respectivamente. De los nuevos lenguajes que se han creado desde cero para compilar en código de bytes de Java, Clojure , Groovy , Scala y Kotlin pueden ser los más populares. Una característica destacable de los lenguajes JVM es que son compatibles entre sí , de modo que, por ejemplo, las bibliotecas Scala se pueden utilizar con programas Java y viceversa. [8]

Java 7 JVM implementa JSR 292: Soporte de lenguajes escritos dinámicamente [9] en la plataforma Java, una nueva característica que admite lenguajes escritos dinámicamente en la JVM. Esta característica se desarrolla dentro del proyecto Da Vinci Machine cuya misión es extender la JVM para que admita lenguajes distintos a Java. [10] [11]

verificador de código de bytes

Una filosofía básica de Java es que es inherentemente seguro desde el punto de vista de que ningún programa de usuario puede bloquear la máquina host o interferir inapropiadamente con otras operaciones en la máquina host, y que es posible proteger ciertos métodos y estructuras de datos que pertenecen a sitios confiables. código del acceso o corrupción por código no confiable que se ejecuta dentro de la misma JVM. Además, no se permite que ocurran errores comunes del programador que a menudo conducen a corrupción de datos o comportamiento impredecible, como acceder desde el final de una matriz o usar un puntero no inicializado. Varias características de Java se combinan para brindar esta seguridad, incluido el modelo de clase, el montón de recolección de basura y el verificador.

La JVM verifica todo el código de bytes antes de ejecutarlo. Esta verificación consta principalmente de tres tipos de controles:

Las dos primeras de estas comprobaciones se llevan a cabo principalmente durante el paso de verificación que ocurre cuando una clase se carga y se hace elegible para su uso. El tercero se realiza principalmente de forma dinámica, cuando otra clase accede por primera vez a elementos de datos o métodos de una clase.

El verificador permite sólo algunas secuencias de código de bytes en programas válidos, por ejemplo, una instrucción de salto (ramificación) sólo puede apuntar a una instrucción dentro del mismo método . Además, el verificador garantiza que cualquier instrucción dada opere en una ubicación de pila fija, [12] permitiendo que el compilador JIT transforme los accesos a la pila en accesos a registros fijos. Debido a esto, que la JVM sea una arquitectura stack no implica una penalización de velocidad para la emulación en arquitecturas basadas en registros cuando se utiliza un compilador JIT. Frente a la arquitectura JVM con código verificado, para un compilador JIT no hay diferencia si recibe registros imaginarios con nombre o posiciones de pila imaginarias que deben asignarse a los registros de la arquitectura de destino. De hecho, la verificación de código diferencia a la JVM de una arquitectura de pila clásica, cuya emulación eficiente con un compilador JIT es más complicada y normalmente la lleva a cabo un intérprete más lento. Además, el intérprete utilizado por la JVM predeterminada es un tipo especial conocido como intérprete de plantilla, que traduce el código de bytes directamente al lenguaje de máquina nativo basado en registros en lugar de emular una pila como un intérprete típico. [13] En muchos aspectos, el HotSpot Interpreter puede considerarse un compilador JIT en lugar de un verdadero intérprete, es decir, la arquitectura de pila a la que apunta el código de bytes no se utiliza realmente en la implementación, sino simplemente una especificación para la representación intermedia que bien puede implementarse. en una arquitectura basada en registros. Otro ejemplo de una arquitectura de pila que es simplemente una especificación e implementada en una máquina virtual basada en registros es Common Language Runtime . [14]

La especificación original para el verificador de código de bytes utilizaba un lenguaje natural que estaba incompleto o incorrecto en algunos aspectos. Se han realizado varios intentos para especificar la JVM como un sistema formal. Al hacer esto, se puede analizar más a fondo la seguridad de las implementaciones actuales de JVM y prevenir posibles vulnerabilidades de seguridad. También será posible optimizar la JVM omitiendo comprobaciones de seguridad innecesarias, si se demuestra que la aplicación que se ejecuta es segura. [15]

Ejecución segura de código remoto

Una arquitectura de máquina virtual permite un control muy detallado sobre las acciones que el código dentro de la máquina puede realizar. Asume que el código es "semánticamente" correcto, es decir, pasó con éxito el proceso (formal) de verificación de código de bytes, materializado por una herramienta, posiblemente fuera de la máquina virtual. Está diseñado para permitir la ejecución segura de código no confiable desde fuentes remotas, un modelo utilizado por los subprogramas de Java y otras descargas de código seguras. Una vez verificado el código de bytes, el código descargado se ejecuta en una " zona de pruebas " restringida, que está diseñada para proteger al usuario contra códigos maliciosos o mal comportamiento. Como complemento al proceso de verificación del código de bytes, los editores pueden comprar un certificado con el que firmar digitalmente los subprogramas como seguros, dándoles permiso para pedirle al usuario que salga del entorno limitado y acceda al sistema de archivos local, al portapapeles y ejecute piezas de software externas. o red.

La industria Javacard ha realizado pruebas formales de verificadores de código de bytes (Desarrollo formal de un verificador integrado para código de bytes de tarjetas Java [16] )

Intérprete de código de bytes y compilador justo a tiempo

Para cada arquitectura de hardware se necesita un intérprete de código de bytes de Java diferente . Cuando una computadora tiene un intérprete de código de bytes de Java, puede ejecutar cualquier programa de código de bytes de Java, y el mismo programa se puede ejecutar en cualquier computadora que tenga dicho intérprete.

Cuando un intérprete ejecuta el código de bytes de Java, la ejecución siempre será más lenta que la ejecución del mismo programa compilado en lenguaje de máquina nativo. Este problema se mitiga mediante compiladores justo a tiempo (JIT) para ejecutar código de bytes de Java. Un compilador JIT puede traducir el código de bytes de Java al lenguaje de máquina nativo mientras ejecuta el programa. Las partes traducidas del programa pueden ejecutarse mucho más rápidamente de lo que podrían interpretarse. Esta técnica se aplica a aquellas partes de un programa que se ejecutan con frecuencia. De esta forma, un compilador JIT puede acelerar significativamente el tiempo total de ejecución.

No existe una conexión necesaria entre el lenguaje de programación Java y el código de bytes de Java. Un programa escrito en Java se puede compilar directamente en el lenguaje de máquina de una computadora real y los programas escritos en otros lenguajes además de Java se pueden compilar en código de bytes de Java.

El código de bytes de Java está diseñado para ser seguro e independiente de la plataforma. [17] Algunas implementaciones de JVM no incluyen un intérprete, sino que consisten únicamente en un compilador justo a tiempo. [18]

JVM en el navegador web

Al comienzo de la vida útil de la plataforma Java, la JVM se comercializó como una tecnología web para crear aplicaciones web enriquecidas . A partir de 2018 , la mayoría de los navegadores web y sistemas operativos que incluyen navegadores web no incluyen un complemento de Java ni permiten la carga lateral de ningún complemento que no sea Flash . El complemento del navegador Java quedó obsoleto en JDK 9. [19]

El complemento del navegador NPAPI Java fue diseñado para permitir que la JVM ejecute los llamados subprogramas Java integrados en páginas HTML. Para los navegadores con el complemento instalado, el subprograma puede dibujar en una región rectangular en la página que se le ha asignado. Dado que el complemento incluye una JVM, los subprogramas de Java no están restringidos al lenguaje de programación Java; cualquier idioma destinado a la JVM puede ejecutarse en el complemento. Un conjunto restringido de API permite que los subprogramas accedan al micrófono del usuario o a la aceleración 3D, aunque los subprogramas no pueden modificar la página fuera de su región rectangular. Adobe Flash Player , la principal tecnología competidora, funciona de la misma manera en este sentido.

En junio de 2015, según W3Techs, el uso de subprogramas Java y Silverlight había caído al 0,1% cada uno para todos los sitios web, mientras que Flash había caído al 10,8%. [20]

JVM e intérpretes de JavaScript

Desde mayo de 2016, JavaPoly permite a los usuarios importar bibliotecas Java no modificadas e invocarlas directamente desde JavaScript. JavaPoly permite que los sitios web utilicen bibliotecas Java no modificadas, incluso si el usuario no tiene Java instalado en su computadora. [21]

Transpilación a JavaScript

Con las continuas mejoras en la velocidad de ejecución de JavaScript, combinadas con el mayor uso de dispositivos móviles cuyos navegadores web no implementan soporte para complementos, se están realizando esfuerzos para dirigirse a esos usuarios mediante la transpilación a JavaScript. Es posible transpilar el código fuente o el código de bytes de JVM a JavaScript.

La compilación del código de bytes de JVM, que es universal en todos los lenguajes JVM, permite aprovechar el compilador existente del lenguaje en código de bytes. El principal código de bytes de JVM para transpiladores de JavaScript es TeaVM, [22] el compilador contenido en Dragome Web SDK, [23] Bck2Brwsr, [24] y j2js-compiler. [25]

Los principales transpiladores de lenguajes JVM a JavaScript incluyen el transpilador de Java a JavaScript contenido en Google Web Toolkit , Clojurescript (Clojure), GrooScript (Apache Groovy), Scala.js (Scala) y otros. [26]

Ver también

Referencias

  1. ^ yan (24 de junio de 2023). "Notas de la versión de JDK 20". Corporación Oráculo . Archivado desde el original el 9 de julio de 2021 . Consultado el 24 de junio de 2023 .
  2. ^ Bill Venners, Dentro de la máquina virtual Java Archivado el 25 de enero de 2021 en Wayback Machine Capítulo 5
  3. ^ "Programa Java Community Process (SM) - JSR: solicitudes de especificación de Java - detalle JSR n.° 924". Jcp.org. Archivado desde el original el 24 de diciembre de 2020 . Consultado el 26 de junio de 2015 .
  4. ^ "Programa Java Community Process (SM) - JSR: solicitudes de especificación de Java - detalle JSR n.º 202". Jcp.org. Archivado desde el original el 26 de febrero de 2012 . Consultado el 26 de junio de 2015 .
  5. ^ La especificación de la máquina virtual Java Archivada el 9 de julio de 2008 en Wayback Machine (la primera edición Archivada el 12 de octubre de 2008 en Wayback Machine y la segunda Archivada el 25 de septiembre de 2011 en Wayback Machine también están disponibles en línea).
  6. ^ "Capítulo 2. La estructura de la máquina virtual Java". Archivado desde el original el 15 de septiembre de 2021 . Consultado el 15 de septiembre de 2021 .
  7. ^ "La especificación de la máquina virtual Java: edición Java SE 7" (PDF) . Docs.oracle.com. Archivado (PDF) desde el original el 4 de febrero de 2021 . Consultado el 26 de junio de 2015 .
  8. ^ "Preguntas frecuentes: interoperabilidad de Java". scala-lang.org . Archivado desde el original el 9 de agosto de 2020 . Consultado el 18 de noviembre de 2015 .
  9. ^ "Programa Java Community Process (SM) - JSR: solicitudes de especificación de Java - detalle JSR n.° 292". Jcp.org. Archivado desde el original el 20 de diciembre de 2020 . Consultado el 26 de junio de 2015 .
  10. ^ "Proyecto Máquina Da Vinci". Openjdk.java.net. Archivado desde el original el 11 de noviembre de 2020 . Consultado el 26 de junio de 2015 .
  11. ^ "Nueva función de JDK 7: compatibilidad con lenguajes escritos dinámicamente en la máquina virtual Java". Oracle.com. Archivado desde el original el 13 de septiembre de 2018 . Consultado el 26 de junio de 2015 .
  12. ^ "El proceso de verificación". La especificación de la máquina virtual Java . Microsistemas solares. 1999. Archivado desde el original el 21 de marzo de 2011 . Consultado el 31 de mayo de 2009 .
  13. ^ "Descripción general del tiempo de ejecución de HotSpot: intérprete". AbiertoJDK . Archivado desde el original el 21 de mayo de 2022 . Consultado el 24 de mayo de 2021 .
  14. ^ "¿Por qué no hacer que CLR esté basado en registros? · Número 4775 · dotnet/runtime". GitHub . Archivado desde el original el 2023-04-20 . Consultado el 24 de mayo de 2021 .
  15. ^ Freund, Stephen N.; Mitchell, John C. (1999). "Un marco formal para el verificador y lenguaje de código de bytes Java". Actas de la 14ª conferencia ACM SIGPLAN sobre programación, sistemas, lenguajes y aplicaciones orientados a objetos - OOPSLA '99 . págs. 147-166. CiteSeerX 10.1.1.2.4663 . doi :10.1145/320384.320397. ISBN  978-1581132380. S2CID  14302964.
  16. ^ Casset, Ludovic; Burdy, Lilian; Requet, Antoine (10 de abril de 2002). "Desarrollo formal de un verificador integrado para código de bytes de tarjeta Java" (PDF) . Inria - Instituto Nacional de Investigación en Ciencia y Tecnología Digital de la Universidad Costa Azul . Archivado (PDF) desde el original el 3 de octubre de 2022.
  17. ^ David J. Eck, Introducción a la programación con Java Archivado el 11 de octubre de 2014 en Wayback Machine , séptima edición, versión 7.0, agosto de 2014 en la sección 1.3 "La máquina virtual Java"
  18. ^ Introducción a Oracle JRockit Archivado el 6 de septiembre de 2015 en Wayback Machine versión R28 en 2. "Comprensión de la compilación y optimización justo a tiempo"
  19. ^ "Oracle desaprueba el complemento del navegador Java y se prepara para su desaparición". Ars Técnica . 28 de enero de 2016. Archivado desde el original el 8 de abril de 2016 . Consultado el 15 de abril de 2016 .
  20. ^ "Tendencias históricas anuales en el uso de lenguajes de programación del lado del cliente, junio de 2015". W3techs.com . Consultado el 26 de junio de 2015 .
  21. ^ Krill, Paul (13 de mayo de 2016). "JavaPoly.js importa código Java existente y lo invoca directamente desde JavaScript". InfoMundo . Archivado desde el original el 25 de julio de 2016 . Consultado el 18 de julio de 2016 .
  22. ^ "Página de inicio del proyecto TeaVM". Teavm.org. Archivado desde el original el 27 de junio de 2015 . Consultado el 26 de junio de 2015 .
  23. ^ "SDK web de Dragome". Dragome.com. Archivado desde el original el 1 de agosto de 2015 . Consultado el 26 de junio de 2015 .
  24. ^ "Bck2Brwsr - APIDesign". Wiki.apidesign.org. Archivado desde el original el 27 de junio de 2015 . Consultado el 26 de junio de 2015 .
  25. ^ Wolfgang Kuehn (decatur). j2js-compiler Archivado el 29 de septiembre de 2013 en Wayback Machine GitHub
  26. ^ "Lista de lenguajes que se compilan en JS · jashkenas/coffeescript Wiki · GitHub". Github.com. 2015-06-19. Archivado desde el original el 31 de enero de 2020 . Consultado el 26 de junio de 2015 .