Máquina virtual de Java

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar
Java Virtual machine
Descripción general de una arquitectura Java virtual machine (JVM) basada en la especificación Java Virtual Machine Java SE 7 Edition

Una máquina virtual Java (JVM) es una máquina virtual que permite que una computadora ejecute programas Java, así como programas escritos en otros lenguajes que también se compilan en Java. código de bytes. 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 de Java en diferentes implementaciones, de modo que los autores de programas que utilizan el Kit de desarrollo de Java (JDK) no necesitan preocuparse por las idiosincrasias de la plataforma de hardware subyacente.

La implementación de referencia de JVM está 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 de Oracle se basan en el tiempo de ejecución de OpenJDK. Eclipse OpenJ9 es otra JVM de código abierto para OpenJDK.

Especificación de JVM

La máquina virtual Java es una computadora abstracta (virtual) definida por una especificación. Es una parte del entorno de tiempo de ejecución de Java. No se especifica el algoritmo de recolección de elementos no utilizados ni ninguna optimización interna de las instrucciones de la máquina virtual Java (su traducción a código de máquina). La razón principal de esta omisión es no restringir innecesariamente a los implementadores. Cualquier aplicación Java puede ejecutarse solo dentro de alguna implementación concreta de la especificación abstracta de la máquina virtual Java.

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

Tenemos la intención de que esta especificación debe documentar suficientemente la máquina virtual de Java para hacer posibles implementaciones de limpieza compatibles. Oracle ofrece pruebas que verifican el funcionamiento adecuado de las implementaciones de la máquina virtual de Java.

Una de las JVM de Oracle se llama HotSpot; el otro, heredado de BEA Systems, es JRockit. Oracle es propietario de la marca registrada Java y puede permitir su uso para certificar conjuntos de implementación como totalmente compatibles con la especificación 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 ser capaz de reconocer y cargar todo lo que se ajuste al formato de archivo de clase de Java. Cualquier implementación es libre de reconocer otras formas binarias además de los archivos class, pero debe reconocer los archivos class.

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

  1. Carga: encuentra e importa los datos binarios para un tipo
  2. Enlace: realiza verificación, preparación y resolución (opcional)
    • Verificación: asegura la corrección del tipo importado
    • Preparación: asigna memoria para variables de clase e inicializa la memoria a valores predeterminados
    • Resolución: transforma referencias simbólicas del tipo en referencias directas.
  3. Iniciación: invoca código Java que inicializa variables de clase a sus valores de inicio adecuados.

En general, hay tres tipos de cargadores de clases: cargadores de clases de arranque, cargadores de clases de extensión y cargadores 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 de confianza, así como un cargador de clases de extensión o un cargador de clases de aplicación. La especificación de la máquina virtual de Java no especifica cómo un cargador de clases debe ubicar las clases.

Arquitectura de máquinas virtuales

JVM opera en tipos específicos de datos como se especifica en las especificaciones de la máquina virtual de Java. Los tipos de datos se pueden dividir en tipos primitivos (enteros, punto flotante, largos, etc.) y tipos de referencia. La JVM anterior era solo una máquina de 32 bits. Los tipos long y double, que son de 64 bits, se admiten de forma nativa, pero consumen dos unidades de almacenamiento en las variables locales de un marco o en la pila de operandos, ya que cada unidad es de 32 bits. Los tipos boolean, byte, short y char son todos de signo extendido (excepto char que se extiende a cero) y funciona como enteros de 32 bits, lo mismo que los tipos int. Los tipos más pequeños solo tienen algunas instrucciones específicas del tipo para cargar, almacenar y convertir tipos. boolean se utiliza como valores byte de 8 bits, donde 0 representa false y 1 representa true. (Aunque booleano ha sido tratado como un tipo desde que La Especificación de Máquina Virtual Java, Segunda Edición aclaró este problema, en el código compilado y ejecutado hay poca diferencia entre un boolean y un byte excepto por la modificación de nombres en firmas de métodos y el tipo de matrices booleanas. booleans en firmas de métodos se modifican como Z mientras que los bytes se modifican como B. Las matrices booleanas llevan el tipo boolean[] pero usan 8 bits por elemento, y la JVM tiene 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 las matrices byte. En todos los demás usos, el tipo boolean 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 recientes de JVM (OpenJDK HotSpot JVM) admiten 64 bits, puede tener 32 -bit/JVM de 64 bits en una de 64 bits sistema operativo 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, hay un impacto en el rendimiento al usar JVM de 64 bits en comparación con JVM de 32 bits.

La JVM tiene un montón de basura recolectada para almacenar objetos y arreglos. 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 basura. Cada subproceso de 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 operandos en cálculos y para recibir 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 de métodos. Por lo tanto, la JVM es tanto una máquina de pila como una máquina de registro. (En la práctica, HotSpot elimina por completo cada pila además del subproceso nativo/pila de llamadas incluso cuando se ejecuta en modo interpretado, ya que su intérprete de plantillas es técnicamente un compilador disfrazado)

Instrucciones de código de bytes

La JVM tiene instrucciones para los siguientes grupos de tareas:

  • Carga y tienda
  • Arithmetic
  • Conversión de tipo
  • Creación y manipulación de objetos
  • Gestión de pilas de Operand (push / pop)
  • Transferencia de control (marcación)
  • Método de invocación y retorno
  • Extrayendo excepciones
  • Compromiso basado en monitores

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 el 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 en un conjunto de tipos de datos abstractos en lugar de los tipos de datos nativos de cualquier arquitectura de conjunto de instrucciones específico.

Lenguajes JVM

Un lenguaje JVM es cualquier lenguaje con funcionalidad que se puede expresar en términos de un archivo de clase válido que se puede alojar en la máquina virtual de Java. Un archivo de clase contiene instrucciones de la máquina virtual de 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.

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 a bytecode Java, Clojure, Apache 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.

Java 7 JVM implementa JSR 292: Compatibilidad con lenguajes tipificados dinámicamente en la plataforma Java, una nueva función que admite lenguajes tipificados dinámicamente en la JVM. Esta función se desarrolla dentro del proyecto Da Vinci Machine, cuya misión es extender la JVM para que admita otros lenguajes además de Java.

Verificador de código de bytes

Una filosofía básica de Java es que es intrínsecamente seguro desde el punto de vista de que ningún programa de usuario puede bloquear la máquina host o interferir de forma inapropiada con otras operaciones en la máquina host, y que es posible proteger ciertos métodos y estructuras de datos. pertenecientes a código confiable del acceso o corrupción por código no confiable que se ejecuta dentro de la misma JVM. Además, no se permite que se produzcan errores comunes de programadores que a menudo provocaban daños en los datos o un comportamiento impredecible, como acceder desde el final de una matriz o utilizar un puntero no inicializado. Varias características de Java se combinan para proporcionar esta seguridad, incluido el modelo de clase, el montón de recolección de elementos no utilizados y el verificador.

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

  • Las sucursales siempre están en lugares válidos
  • Los datos siempre se inicializan y las referencias siempre son de tipo seguro
  • El acceso a datos y métodos privados o de paquetes es controlado rígidamente

Las dos primeras de estas comprobaciones tienen lugar principalmente durante el paso de verificación que se produce cuando se carga una clase y se hace apta 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 solo algunas secuencias de código de bytes en programas válidos, p. una instrucción de salto (bifurcación) solo puede apuntar a una instrucción dentro del mismo método. Además, el verificador asegura que cualquier instrucción dada opere en una ubicación de pila fija, lo que permite que el compilador JIT transforme los accesos a la pila en accesos a registros fijos. Debido a esto, que la JVM sea una arquitectura de pila no implica una penalización de velocidad para la emulación en arquitecturas basadas en registros cuando se usa un compilador JIT. Frente a la arquitectura JVM verificada por código, no hace ninguna diferencia para un compilador JIT si recibe nombres de registros imaginarios o posiciones de pila imaginarias que deben asignarse a los registros de la arquitectura de destino. De hecho, la verificación del código hace que la JVM sea diferente de una arquitectura de pila clásica, cuya emulación eficiente con un compilador JIT es más complicada y, por lo general, 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 (en muchos aspectos, el intérprete HotSpot puede considerarse un compilador JIT en lugar de un verdadero intérprete), lo que significa que la arquitectura de pila a la que se dirige el código de bytes no se usa 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 (Otra instancia de una pila arquitectura siendo simplemente una especificación e implementada en una máquina virtual basada en registros es Common Language Runtime).

La especificación original para el verificador de código de bytes usaba un lenguaje natural que estaba incompleto o era incorrecto en algunos aspectos. Se han realizado varios intentos para especificar la JVM como un sistema formal. Al hacer esto, la seguridad de las implementaciones actuales de JVM se puede analizar más a fondo y se pueden 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 está ejecutando es segura.

Ejecución segura de código remoto

La arquitectura de una máquina virtual permite un control muy detallado sobre las acciones que puede realizar el código dentro de la máquina. Asume que el código es "semánticamente" correcto, es decir, pasó con éxito el proceso de verificación de código de bytes (formal), materializado por una herramienta, posiblemente externa a la máquina virtual. Esto está diseñado para permitir la ejecución segura de código que no es de confianza desde fuentes remotas, un modelo utilizado por los applets de Java y otras descargas de código seguras. Una vez verificado el código de bytes, el código descargado se ejecuta en un "sandbox" restringido, que está diseñado para proteger al usuario contra el mal comportamiento o código malicioso. Como una adición al proceso de verificación de código de bytes, los editores pueden comprar un certificado con el que firmar applets digitalmente como seguros, lo que les da permiso para pedirle al usuario que salga de la zona de pruebas y acceda al sistema de archivos local, portapapeles, ejecutar piezas de software externas o red.

La industria de Javacard ha realizado una prueba formal de los verificadores de código de bytes (Desarrollo formal de un verificador integrado para el código de bytes de la tarjeta Java)

Intérprete de bytecode y compilador justo a tiempo

Para cada arquitectura de hardware se necesita un intérprete de bytecode 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 el 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 entonces ejecutarse mucho más rápido 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 de ejecución general.

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 que no sean Java se pueden compilar en el código de bytes de Java.

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

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 se entregan con 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.

El complemento del navegador Java NPAPI se diseñó para permitir que la JVM ejecute los llamados applets de Java incrustados 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 asignó. Debido a que el complemento incluye una JVM, los subprogramas 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 applets accedan al micrófono del usuario o a la aceleración 3D, aunque los applets 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 de Java y Silverlight había caído un 0,1 % cada uno para todos los sitios web, mientras que Flash había caído un 10,8 %.

JVM e intérpretes de JavaScript

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

Compilació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 a través de la compilación en JavaScript. Es posible compilar el código fuente o el código de bytes JVM en JavaScript.

Compilar el código de bytes de JVM, que es universal en todos los lenguajes de JVM, permite desarrollar el código de bytes del compilador existente del lenguaje. El código de bytes principal de JVM para los compiladores de JavaScript son TeaVM, el compilador contenido en Dragome Web SDK, Bck2Brwsr y j2js-compiler.

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

Contenido relacionado

Apretón de manos (informática)

Programación lógica

Experto en Sistemas

Más resultados...
Tamaño del texto:
Copiar