Partes internas de JVM, Parte 1 - Cargador de clases

Se preparó una traducción del artículo específicamente para estudiantes del curso Java Developer .




En esta serie de artículos, hablaré sobre cómo funciona la máquina virtual Java. Hoy nos fijamos en el mecanismo para cargar clases en la JVM .

La máquina virtual Java está en el corazón del ecosistema tecnológico de Java. Permite que los programas Java implementen el principio "escribir una vez ejecutado en todas partes". Al igual que otras máquinas virtuales, la JVM es una computadora abstracta. La tarea principal de la JVM es cargar archivos de clase y ejecutar el código de bytes contenido en ellos.

La JVM incluye varios componentes, como un cargador de clases , Garbage Collector (gestión automática de memoria), un intérprete, un compilador JIT y componentes de control de flujo. En este artículo, veremos el cargador de clases.

El cargador de clases carga archivos de clase tanto para su aplicación como para la API de Java. Solo los archivos de clase Java API que realmente se requieren al ejecutar el programa se cargan en la máquina virtual.

El código de bytes es ejecutado por el motor de ejecución.



¿Qué es la carga de clases?


La carga de clases es la búsqueda y carga de tipos (clases e interfaces) dinámicamente durante la ejecución del programa. Los datos de tipo están en archivos de clase binarios.

Pasos de carga de clase


El subsistema del cargador de clases no solo es responsable de encontrar e importar datos de clase binaria. También realiza la validación de clases importadas, asigna e inicializa memoria para variables de clase y ayuda a resolver enlaces simbólicos. Estas acciones se realizan en el siguiente orden:

  • Carga : busca e importa datos binarios para un tipo por su nombre, creando una clase o interfaz a partir de esta representación binaria.
  • Vinculación (vinculación) : verificación, preparación y, opcionalmente, permiso:
    • Verificación : verificación de la corrección del tipo importado.
    • Preparación : asignación de memoria para variables de clase estática e inicialización de memoria con valores predeterminados.
    • Resolución : convierta enlaces de tipo simbólico en enlaces directos.
  • La inicialización es una llamada al código Java que inicializa las variables de clase con sus valores iniciales correctos.

Nota: el cargador de clases, además de cargar clases, también es responsable de encontrar recursos. Un recurso son algunos datos (por ejemplo, un archivo ".class", datos de configuración, imágenes) que se identifican mediante una ruta abstracta separada por un carácter "/". Los recursos generalmente se empaquetan con una aplicación o biblioteca para que puedan usarse en el código de la aplicación o biblioteca.

Mecanismo de carga de clases en Java


Nota del traductor: esta sección describe el comportamiento de java <9, en java 9+ se han producido pequeños cambios, que se describen a continuación.

Java usa el modelo de delegación de carga de clase. La idea básica es que cada cargador de clases tiene un cargador "padre". Cuando se carga una clase , el cargador "delega" la búsqueda de la clase a su padre antes de buscar la clase por sí mismo.

El modelo de delegación del cargador de clases es un gráfico de cargadores que pasan solicitudes de carga entre sí. La raíz de este gráfico es el gestor de arranque bootstrap. Los cargadores de clases se crean con un padre en el que pueden delegar la carga y buscar la clase en los siguientes lugares:

  • Caché
  • El padre
  • Cargador de arranque en sí

El cargador de clases primero verifica si ha cargado la clase anteriormente. Si es así, se devuelve la misma clase que se devolvió la última vez (la clase almacenada en la memoria caché). Si no, el padre tiene la oportunidad de cargar la clase. Estos dos pasos se repiten recursivamente en profundidad. Si el padre devuelve nulo (o arroja una ClassNotFoundException ), entonces el cargador busca la clase por sí mismo.

El cargador que está más cerca de la raíz carga la clase, ya que el derecho a cargar primero la clase siempre se otorga al cargador principal. Esto permite que el cargador vea solo las clases cargadas independientemente, por sus padres o antepasados. No puede ver las clases cargadas por cargadores secundarios.

La API de la plataforma Java SE ha definido históricamente dos cargadores de clase:

Cargador de clases Bootstrap (cargador básico, primario) : carga clases desde el classpath bootstrap.

Cargador de clases del sistema (cargador principal ) : la clase principal para los nuevos cargadores de clases y, como regla, el cargador de clases utilizado para cargar y ejecutar la aplicación.

Cargadores de clase JDK 9+


Cargador de clases de aplicaciones : comúnmente utilizado para cargar clases de aplicaciones desde un classpath. También es el gestor de arranque predeterminado para algunos módulos JDK que contienen utilidades o exportan la API de utilidades. ( Nota del traductor: por ejemplo, jdk.jconsole , jdk.jshell , etc. )

Cargador de clase de plataforma : carga módulos Java SE y JDK seleccionados (basados ​​en seguridad / permisos). Por ejemplo, java.sql.

Cargador de clases Bootstrap : carga los módulos principales de Java SE y JDK.

Estos tres cargadores de clase integrados funcionan juntos de la siguiente manera:

  • El cargador de clases de aplicaciones primero busca módulos con nombre definidos para todos los cargadores integrados. Si se define un módulo adecuado para uno de estos cargadores, este cargador carga la clase. Si la clase no se encuentra en el módulo con nombre definido para uno de estos cargadores, el cargador de clases de la aplicación la delega al padre. Si el padre no encuentra la clase, el cargador de clases de la aplicación la busca en el classpath. Las clases encontradas en el classpath se cargan como miembros del módulo sin nombre de este cargador.
  • El cargador de clases de plataforma busca módulos con nombre definidos para todos los cargadores integrados. Si se define un módulo adecuado para uno de estos cargadores, este cargador carga la clase. Si la clase no se encuentra en el módulo con nombre definido para uno de estos cargadores, el cargador de clase de plataforma la delega al padre.
  • El cargador de clases bootstrap busca módulos con nombre definidos por sí mismo. Si la clase no se encuentra en el módulo con nombre definido para el gestor de arranque bootstrap, entonces el gestor de arranque bootstrap busca archivos y directorios agregados al classpath bootstrap utilizando el parámetro -Xbootclasspath / a (le permite agregar archivos y directorios al classpath bootstrap). Las clases encontradas en el classpath de bootstrap se cargan como miembros del módulo sin nombre de este cargador.

Para ver los cargadores de clase integrados, puede usar el siguiente código:

 package ru.deft.homework; import java.sql.Date; public class BuiltInClassLoadersDemo { public static void main(String[] args) { BuiltInClassLoadersDemo demoObject = new BuiltInClassLoadersDemo(); ClassLoader applicationClassLoader = demoObject.getClass().getClassLoader(); printClassLoaderDetails(applicationClassLoader); // java.sql classes are loaded by platform classloader java.sql.Date now = new Date(System.currentTimeMillis()); ClassLoader platformClassLoder = now.getClass().getClassLoader(); printClassLoaderDetails(platformClassLoder); // java.lang classes are loaded by bootstrap classloader ClassLoader bootstrapClassLoder = args.getClass().getClassLoader(); printClassLoaderDetails(bootstrapClassLoder); } private static void printClassLoaderDetails(ClassLoader classLoader) { // bootstrap classloader is represented by null in JVM if (classLoader != null) { System.out.println("ClassLoader name : " + classLoader.getName()); System.out.println("ClassLoader class : " + classLoader.getClass().getName()); } else { System.out.println("Bootstrap classloader"); } } } 

Al ejecutar este código en Amazon Corretto 11.0.3 instalado en mí, obtenemos el siguiente resultado:

 ClassLoader name : app ClassLoader class : jdk.internal.loader.ClassLoaders$AppClassLoader ClassLoader name : platform ClassLoader class : jdk.internal.loader.ClassLoaders$PlatformClassLoader Bootstrap classloader 

Puede obtener más información sobre la API de ClassLoader aquí (JDK 11) .

Source: https://habr.com/ru/post/468193/


All Articles