La Máquina Virtual Java

03/10/2006

Java
La JVM (Java Virtual Machine) es una máquina abstracta software que convive armoniosamente con la máquina con microprocesador real que la alberga, de ahí el nombre de máquina virtual (VM). Los programas desarrollados para la JVM se crean utilizando el lenguaje de programación Java. Este lenguaje, como otros lenguajes de algo nivel, tienen una sintaxis que no está enfocada a un procesador espedífico. Como en otros lenguajes de alto nivel el código fuente Java se compila utilizando una herramienta de copilación pero su salida difiere de la de los compiladores tradicionales.

Las aplicaciones creadas por lenguajes de alto nivel, al contrario de Java, crean un programa ejecutable para el sistema operativo o microprocesador que hay por debajo. Por el contrario, Java crea un programa ejecutable deseñado para la JVM. “Un momento, ¿porqué los programas Java contienen código compilado creado exclusivamente para la JVM cuando la JVM se alberga en un microprocesador con su propio conjunto de instrucciones de lenguaje máquina?” Esa es una muy buena pregunta, y la respuesta nos mostrará cómo Sun resolvió el obstáculo de la portabilidad.

Ya que la JVM es una Máquina Virutal, comparte las mismas características que los micropocesadores, respecto a tener registros, pilas, punteros de intrucciones, un juego de instrucciones, y demás. Este diseño crea una VM (Virtual Machine) que posee las características de una máquina hardware real, y como una máquina, la JVM pude ejecutar programas en su propio lenguaje.

Esto significa que si los desarrolladores de software escriben programas para la JVM, los programas se comportarán del mismo modo, independientemente del microprocesador en el que resida la JVM.

“¿Cómo funcionan los programas Java si están escritos para la JVM y no para el microprocesador?” Los diseñadores de la JVM para su respectivo microprocesador o sistema operativo deben atenerse a las especificación de la JVM y realizar entre bastidores las conexiones necesarias con el microprocesador que está por debajo. Las conexiones entre bastidores permiten a los desarrolladores de software “Escribir una vez, Ejecutar en cualquier parte” (Write Once, Run Anywhere) ya que la JVM se debe comportar igual independientemente del microprocesador subyacente de acuerdo a la especificación del la JVM de Sun. Si un programa está desarrollado para la JVM, entonces se ejecutará en Microsoft Windows, Macintosh, UNIX, Webphones, Network Computers, navegadores de Internet, Web TV, y un incontable número de dispositivos hardware siempre que posean una JVM.

La JVM, como los microprocesadores o los sistemas operativos, contiene información técnica que, debido en su complejidad, llevaría un montón de páginas de un libro. Sin embargo, vamos a ver ciertas áreas de la JVM para obtener una mejor comprensión de cómo funciona por debajo.

Las interioridades de la JVM

La JVM, como un microprocesador real, ustiliza resgistros internos y áreas de memoria para procesar y almacenar datos. Los programas ejecutables Java se crean con una extensión .class y contienen intrucciones (bytecodes) JVM, tabla de símbolos, y otros datos. Al contrario que otros microprocesadores, las demandas de la JVM impone restricciones a los datos del archivo de clase por razones de seguridad. Estas restricciones de seguridad hacen que la tecnología Java sea apreciada por los desarrolladores que desarrollan tanto para Internet como aplicaciones stand-alone gracias a su protección contra aplicaciones maliciosas.

La JVM trabaja con dos tipos de dastos: tipos referencia y tipos primitivos que pueden ser pasados como argumentos, ser devueltos por métodos, y almacenados en variables. Los datos de referencia consisten en: tipos de clase, tipos de interfaz, y tipos array, cuyos valores son referencias a objetos creados dinámicamente de sus tipos respectivos.

Los datos primitivos soportados por la JVM son:

  • byte – enteros de 8-bit con signo en complemento a dos con rango de -128 a 127 (-27 a 27-1), incluidos
  • short – enteros de 16-bit con signo en complemento a dos con rango de -32768 a 32767 (-215 a 215-1), incluidos
  • int – enteros de 32-bit con signo en complemento a dos con rango de -2147483648 a 2147483647 (-231 a 231-1), incluidos
  • long – enteros de 64-bit con signo en complemento a dos con rango de -9223372036854775808 a 9223372036854775807 (-263 a 263-1), incluidos
  • char – enteros sin signo de 16-bit representando caracteres Unicode con rango de ‘u0000’ a ‘uffff’; char no tiene signo, así que ‘uffff’ representa 65535 cuando se pasa a una expresión, no -1
  • float – números IEEE 754 de coma flotante de 32-bit
  • double – números IEEE 754 de coma flotante de 64-bit

Las referencias permiten a la JVM tener un subsistema controlado. Un sistema controlado es seguro ya que las reservas de memoria están oculta para el desarrollador software. Si una aplicación solicita el uso de un bloque de memoria, la JVM devuelve una referencia al área de memoria que usará la aplicación en futuros accesos al área de memoria. La referencia no es un puntero a una dirección de memoria al contrario de lo que ocurre en otros lenguajes como C y C++, sino un manejador de un área de memoria oculto a las aplicaciones. Esto posibilita una característica de seguridad de deshabilita el acceso directo a areas sensibles del subsistema de la JVM por parte de aplicaciones maliciosas o mal escritas.

Los datos primitivos, al contrario de los tipos de referencia, contienen datos reales y no referencias a alguna clase, interface, o array. Las aplicaciones no pueden obtener la dirección de memoria de un tipo primitivo; y por tanto, el subsistema permanece seguro. Al aplicaciones puden leer y escribir en tipos primitivos pero no pueden acceder a ningún puntero basado en un tipo primitivo.

Eliminando la basura

La JVM no posee un mecanismo de liberación de memoria como el free() del lenguaje C o el delete del lenguaje C++. Utiliza un complejo algoritmo llamado “Recolección de Basura”. Cada vendedor de JVM puede proporcional su propio algoritmo de recolección de basura (liberación de recursos usados) siempre que recolecte la basura correctamente. La razón de esta libertad en el algoritmos de recolección de basura es para permitir la libertad del vendedor de JVM en la utilización de las construcciones del lenguaje máquina subyacente para optimizar el rendimiento. La especificación del API (Applications Programming Interface) Java 1.1 proporciona un método para invocar explícitamente al colector de basura desde una aplicación durante su ejecución:

System.gc(); // Invoca al recolector de basura

Otro método para asegurar la recolección de basura en un tipo de referencia explicito es asignarle el valor null después de su utilización:


int array[] = new int[100];
for(int index = 0; index < 100; index++)
array[index] = index;
calculatorRoutine(array);
array = null;
// Indica al recolector de basura que
// libere esta memoria

La implementación normal para la recolección de basura es “recolecta durante el tiempo ocioso”. Las funciones de recolección de basura de la JVM normalmente ocurren duante el tiempo en espera del usuario de la aplicación o tiempos de espera en la ejecución de una aplicación. Mientras que un objeto Class, Interface, o Array mantengan una referncia, la memoria para esa referencia no será recolrectada como basura. Tan pronto como todos los objetos liberan sus referncias a un objeto, se marcará para ser recolectado.

Las áreas de datos de ejecución de la JVM

En la JVM se incluye soporte para multi-hilo (multi-thread). Este soporte multi-hilo se facilita utilizando un registro PC (program counter, contador de programa) para cada hilo de ejecución concurrente. El PC mantienen información durante su ejecución del punto de ejecución en el que se encuentra la aplicación. Cuando la JVM realiza un cambio de hilo, el hilo que se está ejecutando en ese momento se suspende junto con los datos persistente y la última instrucción se mantiene en el PC hasta el momento en el que el hilo continue con su ejecución.

Devido a que la JVM es una VM, ésta también posee una pila privada que se crea en paralelo con un hilo. La pila JVM almacena frames de JVM, almacena variables locales y sus datos, y contribuye a la invocación de métodos como un almacén.

Un frame JVM es una estructura de datos utilizada para almacener datos y resultados, a la vez que para realizar enlaces dinámicos y devolver valores de métodos y para enrutar las excepciones. Los frames comparten la vida de un método y se crean y destruyen cuando se entra y se sale del mismo. Los frames pertenecen a un hilo exclusivamente y no se pueden compartir entre hilos.

La JVM tiene un heap (zona de memoria) comparttida entre todos los hilos, sin embargo. Este heap es la fuente de memoria para los objetos creados dinámicamente en tiempo de ejecución.

Al igual que el área heap, la JVM tiene un área de memoria llamada de métodos (method area). Este área alberga métodos que pueden ser ejecutados por varios hilos. Incluido en el espacio del área de métodos está el pool de constantes de la JVM. El pool de constantes sirve como un tabla de símbolos similar a la de los lenguajes de programación convencionales, excepto que alberga una variedad de datos más rica. El pool de constantes almacena constantes (datos no cambiantes) de literales numéricos y métodos en tiempo de ejecución así como referencias a campos.

La JVM soporta métodos nativos, que son áreas de código dependientes de la máquina que se pueden invocar para su ejecución desde la JVM. Como la llamada a un método nativo transfiere el control, también posee una pila llamada Native Method Stack. El PC no cambia mientras se ejecuta un método nativo ya que no se ejecuta dentro de la JVM. Cuando finaliza el método nativo, el PC continua con la ejecución y se actualiza correspondientemente.

Manejo de Excepciones

El manejo de excepciones se delega desde dentro del la JVM mediante una sentencia throw (lanzar) explícita de la aplicación o por un error ocurrido en tiempo de ejecución. Si no se cpatura un excepción, la pila actual de operacón de métodos y variables locales se eliminan y su frame es desapilado (pop), restaurando el frame del método llamador.

Este proceso continua hacia el siguiente llamador hata que se localiza la sentencia catch (capturar) correspondiente. Si la excepción alcanza el último elemento de la lista de llamadas de método, el hilo se finaliza anormalmente debido a una excepción no resuelta. Si se captura la excepción, la ejecución continua desde el punto del método que captura la excepción y el PC y todas las pilas se ajustan convenientemente.

El juego de instrucciones de la JVM

Los operandos o juegos de instruciones que componen los byte codes son muy pequeños comparados con los operandos existentes para microprocesadores. Una instrucción JVM consiste en un código de operador (opcode) de un byte identificando la acción a realizar, seguido por cero o más operandos proporcionando los argumentos o datos que se utilizarán en la operación. Hay varias instrucciones que no contienen operandos y contienen un opcode.

El la exclusión de las excepciones, el núcleo de ejecución de la JVM se parece al siguiente fragmento de pseudo-código:

do {
getAnOpcode();
if (operands() == true)
getOperands();
executeTheOpcodesAction();
} while (moreProcessingNeeded());

Hay más de 250 opcodes para la JVM (para los más curiosos). Muchos de estos opcodes son similares a los opcodes existentes para las unidades de microprocesadores en lo que respecta a carga, almacenamiento, aritmética, comparación, ramifiación, operaciones de bit, manipulación de pila, conversión, etc. Los opcodes de la JVM son extremadamente numerosos y por eso es por lo que es necesario uan verdadera VM. Existen muchos microprocesadores que no poseen un juego de instrucciones tan rico como el de la JVM.

Vía

5 Responses to “La Máquina Virtual Java”

  1. andresfang Says:

    Ey, el artículo es el único que he encontrado en la web sobre lo que estoy buscando, me parece excelente, gracias por escribir sobre cosas que no podemos encontar casi.
    Aparte que está fácil para entender.

  2. Leandro Says:

    Muchas gracias por la información, es muy valiosa y me ha sacado muchas dudas!
    Saludos

  3. irene Says:

    hola, gracias por el post, esta muy bueno. Pero tengo una inquietud respecto a los hilos manejados por Java. Sabes cuántos hilos soporta en ejecución la máquina virtual de java?. Te agradezco tu pronta respuesta!!.

    bYe


  4. 1. ¿Cómo funciona la JVM?

  5. Pancho Says:

    Hola, gracias por el aporte. una cosa que quisiera que me ayude es saber cuales son los valores recomendados en cuanto a la asignación de memoria de la JVM, es decir, existe algo asi como “La mitad de la memoria disponible” o un tercio de la memoria total, etc.
    agradeciendo de ante mano.


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: