Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, ¡mira aquí! !

Prefacio

Hoy hablaremos sobre el proceso de carga de clases en jvm. Hemos escrito tantas clases pero no conocemos el proceso de carga de clases. ¿No es incómodo?

El inicio de jvm se logra creando una clase inicial mediante el cargador de clases de arranque, que se especifica mediante la implementación específica de jvm. [De las especificaciones oficiales]

Uno de los componentes de jvm es el subsistema cargador de clases, hablaremos de este componente en detalle hoy.

Diagrama de flujo de ejecución de código Java

A través de este diagrama de flujo, todos entienden cómo se ejecuta el código Java que escribimos, lo que implica pasar por el proceso del cargador de clases, así que hablemos de los puntos de conocimiento en él con cuidado.

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

Subsistema de carga de clases

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

Diagrama de arquitectura del sistema de carga de clases

Está bien si no entiendes estas dos imágenes por el momento, sígueme y mira hacia abajo

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

Ciclo de vida de la clase

El ciclo de vida de una clase incluye: carga, vinculación, inicialización, uso y descarga. La carga, vinculación e inicialización pertenecen al proceso de carga de la clase, y lo explicaremos en detalle a continuación. El uso se refiere al uso de nuestro nuevo objeto y la descarga se refiere al objeto que se recolecta como basura.

El proceso de carga de clases

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

  • El primer paso: cargando cargando

Obtenga el .class de la clase a través del nombre completo de la clase (nombre del paquete + nombre de la clase)

Flujo de bytes binarios de archivos

Convierta la estructura de almacenamiento estática representada por el flujo de bytes binarios en la estructura de datos del área de método en tiempo de ejecución

Genere un objeto java.lang.Class que represente la clase en la memoria como la entrada de acceso para varios datos de esta clase en el área de métodos

Resumen: Cargue datos binarios en la memoria -> Asignelos a una estructura que JVM pueda reconocer -> Genere archivos de clases en la memoria.

  • Paso 2: vinculación

La vinculación se refiere al proceso de fusionar la clase creada anteriormente en la máquina virtual Java para que se pueda ejecutar. Se puede dividir en tres etapas: verificación, preparación y análisis.

① Verificar (verificar)

Asegúrese de que la información contenida en el flujo de bytes en el archivo de clase cumpla con los requisitos de la máquina virtual actual y asegúrese de que la clase cargada sea correcta sin poner en peligro la seguridad de la máquina virtual.

② Preparación (Preparar)

Asigne memoria para el campo estático en la clase y establezca el valor inicial predeterminado, por ejemplo, el valor inicial del tipo int es 0. El campo estático modificado por final no se establecerá porque final se asigna en tiempo de compilación

③ Resolver

El propósito de la fase de análisis es convertir las referencias simbólicas en el grupo constante en referencias directas (resolviendo las referencias simbólicas en el grupo constante en referencias reales). Si la referencia del símbolo apunta a una clase descargada, o un campo o método de una clase descargada, entonces la resolución activará la carga de esta clase (pero no necesariamente la vinculación e inicialización de esta clase).

De hecho, las operaciones del analizador a menudo van acompañadas de la ejecución de JVM después de la inicialización. La referencia de símbolo es un conjunto de símbolos para describir el objetivo referenciado. La forma literal de referencia de símbolo está claramente definida en el formato de archivo de clase de la "Especificación de máquina virtual de Java". Una referencia directa es un puntero que apunta directamente al objetivo, un desplazamiento relativo o un identificador que está ubicado indirectamente en el objetivo.

La acción de análisis se dirige principalmente a clases, interfaces, campos, métodos de clase, métodos de interfaz, tipos de método, etc. Correspondiente a CONSTANT_Class_info, CONSTANT_Fieldref_info, CONSTANT_Methodref_info, etc. en el grupo constante.

  • El tercer paso: inicialización

La inicialización es ejecutar el método constructor init de la clase

()el proceso de.

No es necesario definir este método, es una combinación de las acciones de asignación de todas las variables de clase en el compilador javac recopiladas automáticamente y las declaraciones en el bloque de código estático.

Si la clase tiene una clase principal, jvm se asegurará de que el init de la clase principal se ejecute primero y luego se ejecute el init de la subclase.

Clasificación de cargadores de clases

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

  • El primero: clase de inicio / clase de arranque: Bootstrap ClassLoader

Este cargador de clases está implementado en lenguaje C / C ++ y está anidado dentro de la JVM.Los programas Java no pueden manipular directamente esta clase.

Se utiliza para cargar bibliotecas de clases principales de Java, como: JAVA_HOME / jre / lib / rt.jar, resources.jar, paquetes sun.boot.class.path bajo la ruta, que se utilizan para proporcionar los paquetes necesarios para el funcionamiento de jvm.

No se hereda de java.lang.ClassLoader, no tiene cargador de clases padre

Carga el cargador de clases de extensión y el cargador de clases de aplicaciones y se convierte en su cargador de clases padre

Por razones de seguridad, la clase de inicio solo carga clases que comienzan con el nombre del paquete: java, javax, sun

  • Segundo: Cargador de clases de extensión: Cargador de clases de extensión

Escrito en lenguaje Java, implementado por sun.misc.Launcher $ ExtClassLoader, podemos usar programas Java para operar este cargador

Derivado y heredado de java.lang.ClassLoader, el cargador de clases padre es el cargador de clases de inicio

Cargue la biblioteca de clases desde la propiedad del sistema: directorio java.ext.dirs, o cargue la biblioteca de clases desde el directorio de instalación de JDK: directorio jre / lib / ext. Podemos poner nuestro propio paquete en el directorio anterior y se cargará automáticamente.

  • Tercero: Cargador de clases de aplicaciones: Cargador de clases de aplicaciones

Escrito en lenguaje Java, por sun.misc.Launcher $ AppClassLoader

lograr.

Derivado y heredado de java.lang.ClassLoader, el cargador de clases padre es el cargador de clases de inicio

Es responsable de cargar la biblioteca de clases en la ruta especificada por la variable de entorno classpath o la propiedad del sistema java.class.path

Es el cargador de clases predeterminado en el programa, y ​​todas las clases de nuestro programa Java son cargadas por él.

Podemos obtener y operar este cargador a través de ClassLoader # getSystemClassLoader ()

  • Cuarto: cargador personalizado

En circunstancias normales, los tres cargadores anteriores pueden satisfacer nuestro trabajo de desarrollo diario. Cuando no estamos satisfechos, también podemos personalizar los cargadores

Por ejemplo, use la red para cargar clases Java, para garantizar la seguridad de la transmisión, utilizando operaciones de cifrado, luego los tres cargadores anteriores no pueden cargar esta clase, luego debe personalizar el cargador

Pasos de implementación del cargador personalizado

Herede la clase java.lang.ClassLoader y anule el método findClass ()

Si no hay requisitos demasiado complicados, puede heredar directamente la clase URLClassLoader y anular el método loadClass. Para obtener más información, consulte AppClassLoader y ExtClassLoader.

Varias formas de obtener ClassLoader

Es una clase abstracta, todos los cargadores de clases posteriores heredan de ClassLoader (sin incluir el cargador de clases de inicio)

// 方式一:获取当前类的 ClassLoader
clazz.getClassLoader()
// 方式二:获取当前线程上下文的 ClassLoader
Thread.currentThread().getContextClassLoader()
// 方式三:获取系统的 ClassLoader
ClassLoader.getSystemClassLoader()
// 方式四:获取调用者的 ClassLoader
DriverManager.getCallerClassLoader()

Mecanismo de carga de clases-mecanismo de delegación principal

Jvm usa un método de carga bajo demanda para archivos de clase. Cuando la clase necesita ser usada, jvm cargará su archivo de clase en la memoria para generar objetos de clase.

Al cargar la clase, se adopta el mecanismo de delegación padre, es decir, un modo de delegación de tareas que entrega la solicitud a la clase padre para su procesamiento.

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

  • principio de funcionamiento

(1) Si un cargador de clases recibe una solicitud de carga de clases, no la cargará primero y delegará esta solicitud al cargador de clases principal para que la ejecute.

(2) Si la clase principal todavía tiene un cargador de clases principal, continúe delegando hacia arriba hasta el cargador de clases de inicio: Bootstrap ClassLoader

(3) Si el cargador de clases principal puede completar la tarea de carga, devolverá un resultado exitoso. Si la clase principal no se carga, la clase secundaria intentará cargarla. Si la clase secundaria no se carga, lanzará una excepción ClassNotFoundException. Este es el modo de delegación parental

  • Método de carga de paquetes de terceros: mecanismo de delegación inversa

Hay muchas interfaces de proveedor de servicios (SPI) en las aplicaciones Java. Estas interfaces permiten a terceros proporcionar implementaciones para ellas. Por ejemplo, las SPI comunes incluyen JDBC, JNDI, etc. Estas interfaces SPI pertenecen a la biblioteca principal de Java y generalmente existen. En el paquete rt.jar, lo carga el cargador de clases Bootstrap. El cargador de clases Bootstrap no puede cargar directamente la clase de implementación de SPI. Al mismo tiempo, debido a la existencia del modo de delegación parental, el cargador de clases Bootstrap no puede delegar la clase de implementación SPI del cargador AppClassLoader a la inversa. En este caso, necesitamos un cargador de clases especial para cargar bibliotecas de clases de terceros, y el cargador de clases de contexto de subprocesos (el disruptor del modelo de delegación padre) es una buena opción.

Se puede ver en la figura que el paquete principal rt.jar lo carga el cargador de clases Bootstrap, que contiene las clases de la interfaz principal de SPI. Debido a que las clases en SPI a menudo necesitan llamar a los métodos de clases de implementación externas, jdbc.jar contiene clases de implementación externas (jdbc.jar). (jar existe en la ruta de acceso de clases) no puede ser cargado por el cargador de clases Bootstrap, por lo que el cargador de clases de contexto de subprocesos solo puede delegarse para cargar las clases de implementación en jdbc.jar en la memoria para que las clases relacionadas con SPI las usen. Obviamente, este método de carga del cargador de clases de contexto de subprocesos destruye el "modelo de delegación parental". Abandona el modelo de cadena de carga de delegación parental durante la ejecución, de modo que el programa puede usar el cargador de clases a la inversa. Por supuesto, esto también hace que el cargador de clases Java se convierta en Mas flexible.

Cargador de clases JVM, explicación detallada del mecanismo de carga de clases, solo lea este

  • Mecanismo de seguridad sandbox

Personalice la clase String, pero al cargar la clase String personalizada, será el primero en usar el cargador de clases de arranque para cargar, y el cargador de clases de arranque cargará primero los archivos que vienen con el JDK (javalangString en el paquete rt.jar. class), el mensaje de error dice que no hay un método principal debido a la clase String en el paquete rt.jar cargado. Esto puede garantizar la protección del código fuente del núcleo de Java, que es el mecanismo de seguridad de la zona de pruebas.

Supongo que te gusta

Origin blog.csdn.net/doubututou/article/details/109249941
Recomendado
Clasificación