JVM: ¿Qué es y cómo funciona la Máquina Virtual Java?

Fundamentos

La JVM (Java Virtual Machine) actúa como un motor en tiempo de ejecución para ejecutar aplicaciones Java. Conoce aquí la arquitectura de la Máquina Virtual Java!

Como vimos anteriormente en las características de Java, se destaca por ser independiente de la plataforma, y precisamente esto se debe a la JVM, pero ¿qué es realmente la máquina virtual de Java?

Ojo: Puedes saltar esta parte de la Introducción y seguir con tu primer Hola Mundo en Java -Y todo lo que viene luego. Cuando tengas conocimientos básicos sobre las diferentes clases, puedes volver aquí. (Si continuas esta parte sin conocimiento básico, esto te va a resultar complejo)

JVM es la que realmente llama al método principal presente en un código Java. JVM es una parte del JRE (Java Run Environment).

Las aplicaciones Java se llaman WORA (Write Once Run Everywhere). Esto significa que un programador puede desarrollar código Java en un sistema y puede esperar que se ejecute en cualquier otro sistema habilitado para Java sin ningún ajuste. Todo esto es posible gracias a la JVM.

1. Así funciona la JVM

Cuando compilamos un archivo .java, el compilador Java genera un archivo .class (contiene byte-code) con el mismo nombre del archivo. Este archivo .class entra en varios pasos cuando lo ejecutamos. Estos pasos juntos describen toda la arquitectura JVM.

Como funciona la JVM
Cómo funciona la JVM – Fuente de la imagen JVM: https://en.wikipedia.org/wiki/Java_virtual_machine

1.1. Subsistema Java Class Loader

Es principalmente responsable de tres actividades:

  • Carga
  • Enlace
  • Inicialización

1.1.1. Carga

El cargador de clases lee el archivo .class, genera los datos binarios correspondientes y los guarda en el área de métodos (“method area” –vea la imagen anterior). Para cada archivo .class, JVM almacena la siguiente información en el área de método.

  1. Nombre completamente calificado de la clase cargada y su clase primaria inmediata.
  2. Si el archivo .class está relacionado con Class o Interface o Enum
  3. Información sobre modificadores, variables, métodos, etc.

Después de cargar el archivo .class, JVM crea un objeto de tipo Class para representar este archivo en la memoria heap. Tenga en cuenta que este objeto es de tipo Class predefinido en el paquete java.lang. Este objeto Class puede ser utilizado por el programador para obtener información de nivel de clase como nombre de clase, nombre principal, métodos e información de variable, etc. Para obtener esta referencia de objeto, podemos usar el método getClass() de la clase Object.

Por ejemplo:

// Un programa de Java para demostrar el funcionamiento de un objeto de tipo Class
// creado por la JVM para representar el archivo .class en momoria
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
// Código Java para demostrar el uso del objeto Class
// creado por JVM
public class Test
{
    public static void main(String[] args)
    {
        Student s1 = new Student();
 
        // Obtener el objeto Class creado
        // por la JVM.
        Class c1 = s1.getClass();
 
        // Imprimir el tipo de objeto usando c1.
        System.out.println(c1.getName());
 
        // Obteniendo todos los métodos en una matriz
        Method m[] = c1.getDeclaredMethods();
        for (Method method : m)
            System.out.println(method.getName());
 
        // Obteniendo todos los campos en una matriz
        Field f[] = c1.getDeclaredFields();
        for (Field field : f)
            System.out.println(field.getName());
    }
}
 
// Una clase de muestra cuya información se obtiene anteriormente usando
// su objeto class.
class Student
{
    private String name;
    private int roll_No;
 
    public String getName()  {  return name;   }
    public void setName(String name) { this.name = name; }
    public int getRoll_no()  { return roll_No;  }
    public void setRoll_no(int roll_no) {
        this.roll_No = roll_no;
    }
}

Salida:

Student
getName
setName
getRoll_no
setRoll_no
name
roll_No
Nota: por cada archivo .class cargado, solo se crea un objeto Class.
Student s2 = new Student();
// c2 apuntará al mismo objeto donde
// c1 está apuntando
Class c2 = s2.getClass();
System.out.println(c1==c2); // true

1.1.2. Vinculación o Enlace

Realiza la verificación, la preparación y (opcionalmente) la resolución.

  • Verificación: asegura la exactitud del archivo .class, es decir, comprueba si este archivo está formateado correctamente y generado por un compilador válido o no. Si la verificación falla, obtenemos la excepción de tiempo de ejecución java.lang.VerifyError.
  • Preparación: JVM asigna memoria para las variables de clase e inicializa la memoria a los valores predeterminados.
  • Resolución: es el proceso de reemplazar referencias simbólicas del tipo con referencias directas. Se realiza buscando en el área del método (method area) para localizar la entidad a la que se hace referencia.

1.1.3. Inicialización

En esta fase, todas las variables estáticas se asignan con sus valores definidos en el código y en el bloque estático (si corresponde). Esto se ejecuta de arriba a abajo en una clase y de padres a hijos en la jerarquía de clases.

En general, hay tres cargadores de clase (class loaders):

  • Cargador de clases Bootstrap (Bootstrap class loader): cada implementación de JVM debe tener un cargador de clases de arranque, capaz de cargar clases confiables. Carga las clases API centrales de java presentes en el directorio JAVA_HOME/jre/lib. Esta ruta se conoce popularmente como ruta de arranque. Se implementa en lenguajes nativos como C, C++.
  • Cargador de clases de extensiones (Extension class loader): es un elemento secundario del Bootstrap class loader. Carga las clases presentes en los directorios de extensiones JAVA_HOME/jre/lib/ext (Ruta de extensión) o cualquier otro directorio especificado por la propiedad del sistema java.ext.dirs. Está implementado en Java por la clase sun.misc.Launcher$ExtClassLoader.
  • Cargador de clases de sistema/aplicación (system/Application class loader): es secundario del cargador de clases de extensión. Es responsable de cargar las clases desde la ruta de la clase de aplicación. Internamente utiliza la variable de entorno asignada a java.class.path. También se implementa en Java mediante la clase sun.misc.Launcher$AppClassLoader.

Por ejemplo:

// Código Java para demostrar el subsistema de cargador de clases
public class Test
{
    public static void main(String[] args)
    {
        // clase String es cargado por bootstrap loader, y
        // bootstrap loader no es un objeto de Java, por lo tanto nulo
        System.out.println(String.class.getClassLoader());
 
        // La clase Test es cargada por Application loader
        System.out.println(Test.class.getClassLoader());
    }
}

Salida:

null
[email protected]

Nota: JVM sigue el principio Delegación-Jerarquía para cargar clases. El cargador de clases del sistema delega la solicitud de carga al cargador de clases de extensión y la solicitud de delegado del cargador de clases de extensión al cargador de clases bootstrap. Si la clase se encuentra en la ruta boot-strap, la clase se carga; de lo contrario, solicite de nuevo las transferencias al cargador de clases de extensión y luego al cargador de clases del sistema. Por último, si el cargador de clases del sistema no puede cargar la clase, entonces obtenemos la excepción de tiempo de ejecución java.lang.ClassNotFoundException.

Estructura JVM Inicialización
Estructura JVM Inicialización

1.2. Memoria JVM

  • Área de método (Method area): en el área de método, se almacena toda la información del nivel de clase, como el nombre de clase, el nombre inmediato de la clase principal, la información de métodos y variables, etc., incluidas las variables estáticas. Solo hay un área de método por JVM, y es un recurso compartido.
  • Área Heap (Heap area): la información de todos los objetos se almacena en el área heap. También hay un área heap por JVM. También es un recurso compartido.
  • Área de pila (Stack area): para cada subproceso, JVM crea una pila en tiempo de ejecución que se almacena aquí. Cada bloque de esta pila se llama registro de activación/marco de pila que almacena los métodos de llamadas. Todas las variables locales de ese método se almacenan en su marco correspondiente. Una vez que finaliza un hilo, JVM destruirá la pila en tiempo de ejecución. No es un recurso compartido.
  • Registros de PC (PC Registers): Almacena la dirección de la instrucción de ejecución actual de un hilo. Obviamente, cada hilo tiene registros de PC separados.
  • Pilas de métodos nativos (Native method stacks): para cada hilo, se crea una pila nativa separada. Almacena información del método nativo.
Memoria JVM Java
Memoria JVM Java

1.3. Motor de ejecución (Execution Engine)

El motor de ejecución ejecuta .class (bytecode). Lee el código de bytes línea por línea, usa datos e información presente en varias áreas de memoria y ejecuta instrucciones. Se puede clasificar en tres partes:

  • Intérprete: interpreta el bytecode línea por línea y luego lo ejecuta. La desventaja aquí es que cuando se llama un método varias veces, cada vez se requiere interpretación.
  • Compilador Just-In-Time (JIT): se usa para aumentar la eficiencia del intérprete. Compila todo el bytecode y lo cambia a código nativo para que cada vez que el intérprete vea llamadas a métodos repetidos, JIT proporcione código nativo directo para esa parte, de modo que la reinterpretación no es necesario, por lo tanto, se mejora la eficiencia.
  • Recolector de basura: destruye objetos no referenciados.

1.4. Interfaz nativa de Java (JNI – Java Native Interface)

Es una interfaz que interactúa con las Bibliotecas de métodos nativos y proporciona las bibliotecas nativas (C, C++) necesarias para la ejecución. Permite a la JVM llamar a bibliotecas C/C ++ y ser llamado por bibliotecas C/C++ que pueden ser específicas del hardware.

1.5. Bibliotecas de métodos nativos (Native Method Libraries)

Es una colección de bibliotecas nativas (C, C ++) que requiere el motor de ejecución.

Máquina Virtual Java
  • JVM: ¿Qué es y cómo funciona?

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

0 comentarios

  1. […] JVM: ¿Qué es y cómo funciona la Máquina Virtual Java? […]

  2. […] JVM: ¿Qué es y cómo funciona la Máquina Virtual Java? […]

  3. […] ejecutar Java, se utiliza una máquina abstracta llamada Java Virtual Machine (JVM). La JVM ejecuta el bytecode de Java. Entonces, la CPU ejecuta la JVM. Dado que todas las JVM […]

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.