¿Cómo funciona JVM? ¿Arquitectura JVM?

JVM (Java Virtual Machine) actúa como un motor de tiempo de ejecución para ejecutar aplicaciones Java. JVM es el que realmente llama al método principal presente en un código Java. JVM es parte de JRE (Java Runtime Environment).

Las aplicaciones Java se denominan WORA (Write Once Run Anywhere). 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 JVM.

Cuando compilamos un archivo .java , el compilador de Java genera archivos .class (que contienen código de bytes) con los mismos nombres de clase presentes en el archivo .java . Este archivo .class entra en varios pasos cuando lo ejecutamos. Estos pasos juntos describen toda la JVM. 

jvm

 
Subsistema de cargador de clases

Es responsable principalmente de tres actividades. 

  • Cargando
  • Enlace
  • Inicialización

Cargando: El cargador de clases lee el archivo “. class” , genere los datos binarios correspondientes y guárdelos en el área de métodos. Para cada archivo “ .class” , JVM almacena la siguiente información en el área de método. 
 

  • El nombre completo de la clase cargada y su clase principal inmediata.
  • Si el archivo » .class» está relacionado con Class o Interface o Enum.
  • Información sobre modificadores, variables y métodos, etc.

Después de cargar el archivo “ .class” , JVM crea un objeto de tipo Clase para representar este archivo en la memoria del montón. Tenga en cuenta que este objeto es de tipo Clase predefinido en el paquete java.lang . El programador puede usar estos objetos de clase para obtener información de nivel de clase, como el nombre de la clase, el nombre principal, los métodos y la información de variables, etc. Para obtener esta referencia de objeto, podemos usar el método getClass() de la clase de objeto .

Java

// A Java program to demonstrate working
// of a Class type object created by JVM
// to represent .class file in memory.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
  
// Java code to demonstrate use
// of Class object created by JVM
public class Test {
    public static void main(String[] args)
    {
        Student s1 = new Student();
  
        // Getting hold of Class
        // object created by JVM.
        Class c1 = s1.getClass();
  
        // Printing type of object using c1.
        System.out.println(c1.getName());
  
        // getting all methods in an array
        Method m[] = c1.getDeclaredMethods();
        for (Method method : m)
            System.out.println(method.getName());
  
        // getting all fields in an array
        Field f[] = c1.getDeclaredFields();
        for (Field field : f)
            System.out.println(field.getName());
    }
}
  
// A sample class whose information
// is fetched above using its Class object.
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;
    }
}
Producción

Student
getName
setName
getRoll_no
setRoll_no
name
roll_No

Nota: Por cada archivo “ .class” cargado , solo se crea un objeto de la clase. 
 

Student s2 = new Student();
// c2 will point to same object where 
// c1 is pointing
Class c2 = s2.getClass();
System.out.println(c1==c2); // true

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

  • Verificación : Asegura la corrección del archivo .class , es decir, comprueba si este archivo está correctamente formateado 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 . Esta actividad la realiza el componente ByteCodeVerifier. Una vez que se completa esta actividad, el archivo de clase está listo para compilarse.
  • Preparación : JVM asigna memoria para variables de clase e inicializa la memoria a los valores predeterminados.
  • Resolución : Es el proceso de sustitución de referencias simbólicas del tipo por referencias directas. Se realiza buscando en el área de métodos para localizar la entidad referenciada.

Inicialización: En esta fase, todas las variables estáticas se asignan con sus valores definidos en el código y el bloque estático (si lo hay). Esto se ejecuta de arriba a abajo en una clase y de padre a hijo en la jerarquía de clases. 
En general, hay tres cargadores de clases: 
 

  • Cargador de clases de arranque : cada implementación de JVM debe tener un cargador de clases de arranque, capaz de cargar clases de confianza. Carga las clases principales de la API Java presentes en el directorio » JAVA_HOME/jre/lib» . Este camino se conoce popularmente como el camino de arranque. Está implementado en lenguajes nativos como C, C++.
  • Cargador de clases de extensión : es un elemento secundario del cargador de clases de arranque. Carga las clases presentes en los directorios de extensiones “ JAVA_HOME/jre/lib/ext” (ruta de la 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 : es un elemento secundario del cargador de clases de extensión. Es responsable de cargar clases desde el classpath de la aplicación. Utiliza internamente la variable de entorno que se asigna a java.class.path. También se implementa en Java mediante la clase sun.misc.Launcher$AppClassLoader .

Java

// Java code to demonstrate Class Loader subsystem
public class Test {
    public static void main(String[] args)
    {
        // String class is loaded by bootstrap loader, and
        // bootstrap loader is not Java object, hence null
        System.out.println(String.class.getClassLoader());
  
        // Test class is loaded by Application loader
        System.out.println(Test.class.getClassLoader());
    }
}
Producción

null
jdk.internal.loader.ClassLoaders$AppClassLoader@8bcc55f

Nota: JVM sigue el principio de jerarquía de delegación 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 de arranque. Si se encuentra una clase en la ruta de arranque, la clase se carga; de lo contrario, solicite nuevamente 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, obtenemos la excepción de tiempo de ejecución java.lang.ClassNotFoundException

jvm

Memoria JVM 

  1. Área de métodos: en el área de métodos, se almacena toda la información de nivel de clase, como el nombre de la clase, el nombre de la clase principal inmediata, 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.
  2. Área de montón: La información de todos los objetos se almacena en el área de montón. También hay un área de almacenamiento dinámico por JVM. También es un recurso compartido.
  3. Área de pila: 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 llamadas a métodos. Todas las variables locales de ese método se almacenan en su marco correspondiente. Después de que finaliza un subproceso, JVM destruirá su pila en tiempo de ejecución. No es un recurso compartido.
  4. Registros de PC: almacena la dirección de la instrucción de ejecución actual de un hilo. Obviamente, cada subproceso tiene registros de PC separados.
  5. Pilas de métodos nativos: para cada subproceso, se crea una pila nativa separada. Almacena información de métodos nativos. 

jvm2

Motor de ejecución 

El motor de ejecución ejecuta el » .class» (código de bytes). Lee el código de bytes línea por línea, utiliza datos e información presentes 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 un método se llama varias veces, cada vez se requiere interpretación.
  • Compilador Just-In-Time (JIT) : Se utiliza para aumentar la eficiencia de un intérprete. Compila el código de bytes completo y lo cambia a código nativo, de modo que cada vez que el intérprete ve llamadas de método repetidas, JIT proporciona código nativo directo para esa parte, por lo que no se requiere una reinterpretación y, por lo tanto, se mejora la eficiencia.
  • Recolector de basura : Destruye objetos no referenciados. Para obtener más información sobre el recolector de basura, consulte Recolector de basura .

Interfaz nativa de Java (JNI): 

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 que JVM llame a bibliotecas C/C++ y que las bibliotecas C/C++ las llamen, que pueden ser específicas del hardware.

Bibliotecas de métodos nativos: 

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

 
Este artículo es una contribución de Gaurav Miglani . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a contribuir@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *