Notas de lectura "Comprensión profunda de la máquina virtual Java" Capítulo 7 Mecanismo de carga de clases de máquina virtual

Haga clic para ver la colección de lectura y peinado "Comprensión detallada de la máquina virtual Java"

Momento de la carga de la clase

Inserte la descripción de la imagen aquí
El análisis se puede realizar después de la inicialización. Esto es para admitir la función de enlace en tiempo de ejecución del lenguaje java (también llamado enlace dinámico o enlace tardío). Hay y solo los siguientes seis casos, el objeto debe inicializarse inmediatamente (por supuesto, la preparación de verificación de carga se ha completado)
1: Al encontrar las cuatro instrucciones de código de bytes de new getstatic putstatic o invokestatic, si el tipo no se ha inicializado, entonces La fase de inicialización debe activarse primero. El código java típico que puede generar estas cuatro instrucciones es
1. Cuando se usa la palabra clave new para instanciar un objeto
2. Leer o establecer un tipo de variable estática (las variables estáticas modificadas por * final se almacenan en el grupo de constantes en la etapa de compilación, Entonces, excepto para las variables finales)
3. Al llamar a un método estático de un tipo.
2: Llamada reflexiva al tipo. Si el tipo no se inicializa, debe inicializarse inmediatamente.
3: Cuando se inicializa la clase, si su clase principal aún no se ha inicializado, se debe activar la inicialización de la clase principal. primero.
4: Cuando se inicia la máquina virtual, debe especificar una clase principal (método principal) que se ejecutará. La máquina virtual primero inicializa esta clase.
Cinco: Cuando se utiliza el soporte de lenguaje dinámico recién agregado de JDK7, si el análisis final El resultado de una instancia de MethodHandle es REF_getStatic REF-putStatic REF_invokeStatic REF_newInvokeSpecial cuatro tipos El identificador de método del identificador de método y la clase correspondiente a este identificador de método no se ha inicializado, primero debe inicializarlo (gradualmente no sé qué QAQ está hablando)
Seis: Cuando una interfaz define un nuevo método predeterminado agregado por JDK8 (modificación de la palabra clave predeterminada), si la clase de implementación de esta interfaz se inicializa, la interfaz debe inicializarse antes que ella.
Los seis métodos anteriores se denominan referencia activa, todos los demás métodos de referencia no activarán la inicialización y se denominan aplicaciones pasivas.
Ejemplos de referencias pasivas

public class Test1 {
    
    
    static {
    
    
        System.out.println("父类初始化");
    }
    public static  final int finalStaticVal = 123;
    public static  int staticVal = 123;
    public static void fun(){
    
    
        System.out.println("父类静态方法");
    }
}
public class Test2 extends Test1{
    
    
    static {
    
    
        System.out.println("子类初始化");
    }
}

Prueba 1

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(Test2.staticVal);
    }
}

Inserte la descripción de la imagen aquí
Llamar a las variables estáticas de la clase padre a través de la subclase no hace que la subclase se inicialice

Prueba 2

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(Test2.finalStaticVal);
    }
}

Inserte la descripción de la imagen aquí
Se llama a la variable estática final de la clase principal, y la clase principal y la subclase no se inicializan

Prueba 3

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        Test2.fun();
    }
}

Inserte la descripción de la imagen aquí
Llame al método estático de la clase principal, la clase secundaria no se inicializa

Proceso de carga de clases

carga

La máquina virtual debe completar las siguientes tres cosas durante la fase de carga:
1. Obtener el flujo de bytes binarios que define esta clase a través del nombre completo de una clase
2. Convertir la estructura de almacenamiento estática representada por el flujo de bytes en el tiempo de ejecución de el método Estructura de datos
3. Genere un objeto Class que represente esta clase en la memoria, la forma más sencilla de acceder a varios datos de esta clase

La máquina virtual no especifica que el flujo de bytes binarios de la clase debe obtenerse del archivo .class, para ser precisos, no especifica dónde y cómo obtenerlo. Por ejemplo, se
puede obtener de un paquete comprimido ZIP, que es muy común, y eventualmente se convertirá en la base del formato jar EAR WAR en el futuro
. Obtener de la red.
Cálculo y generación del tiempo de ejecución. En este escenario, la tecnología de proxy dinámico
se convierte de otros archivos en (JSP). - "Archivo de clase)
leído de la base de datos y
así sucesivamente. .
La fase de carga se puede completar utilizando el cargador de clases integrado de la máquina virtual o mediante un cargador de clases definido por el usuario.
Parte de las acciones en la fase de carga y la fase de conexión (como parte de las acciones de verificación del formato de archivo de código de bytes) están intercaladas.
Una vez que se completa la fase de carga, el flujo de bytes binarios fuera de la máquina virtual se almacena en el área de métodos de acuerdo con el formato establecido por la máquina virtual.

verificación

El primer paso de la fase de conexión durante la verificación. El propósito de esta fase es garantizar que la información contenida en el flujo de bytes del archivo de clase cumpla con todos los requisitos de restricción de la "Especificación de máquina virtual Java". Asegúrese de que esta información no ponga en peligro la seguridad de la máquina virtual después de ejecutarse como código. El
lenguaje Java en sí es un lenguaje compilado relativamente seguro (el código Java no puede acceder a matrices a través de fronteras y no puede asignar valores a un objeto a voluntad, etc.). Pero estas operaciones se pueden implementar a nivel de código de bytes. El archivo de clase se puede generar de varias formas (por ejemplo, mecanografiado a mano). Si la máquina virtual no verifica el flujo de bytes de entrada y confía plenamente en él, es probable que se cargue con un código de bytes incorrecto o malicioso. El flujo hace que el sistema hable de ataques. La fase de verificación se divide aproximadamente en los siguientes 4 pasos

1. Verificación del formato de archivo

El propósito principal de la fase de verificación es garantizar que el flujo de bytes de entrada se pueda analizar y almacenar correctamente en el área de método, y que el formato cumpla con los requisitos de descripción de información de tipo Java.
Tales como: si comienza con el número mágico 0XCAFEBABE
, si los números de versión mayor y menor están dentro del rango aceptado de la máquina virtual Java actual
. Si las constantes en el grupo de constantes no son compatibles con los tipos de constantes que
apuntan a las constantes Si hay constantes que apuntan a constantes inexistentes o constantes que no se ajustan al tipo,
etc.
Solo el flujo de bytes verificado por el formato de archivo puede ingresar al área de método de la memoria de la máquina virtual Java para almacenamiento, por lo que las siguientes tres etapas se basan en la estructura de almacenamiento del área de método, y no se agregarán más palabras. leído o manipulado.

2. Verificación de metadatos

La segunda etapa es el análisis semántico de la información descrita por el código de bytes,
como si esta clase tiene una clase principal
, si la clase principal se puede heredar
, si la clase no abstracta implementa la interfaz y los métodos abstractos en la clase principal. ,
y así sucesivamente.
El objetivo principal de esta etapa es realizar una verificación semántica de la información de metadatos de la clase para garantizar que no haya información de metadatos que contradiga la definición de la "Especificación del lenguaje Java".

3. Verificación de bytecode

La tercera etapa es la etapa más complicada del proceso de verificación. El objetivo principal es analizar el flujo de datos y controlar el flujo. Asegúrese de que la semántica del programa sea legal y lógica. Después de verificar el tipo de datos en la información de metadatos en la segunda etapa, verifique y analice el cuerpo del método de la clase en esta etapa para asegurarse de que no haya ningún comportamiento que dañe la máquina virtual.
Por ejemplo,
cualquier instrucción de salto en el comprobante no saltará a instrucciones de código de bytes fuera del
cuerpo del método, asegurando que la conversión de tipo en el cuerpo del método sea válida

4. Verificación de la referencia de símbolo

El comportamiento de verificación de la última fase ocurre cuando la máquina virtual convierte la referencia simbólica en una referencia directa, esta acción de conversión se completará en la tercera fase de la conexión, la fase de análisis. La verificación de referencia de símbolo puede verse como una verificación de línea coincidente de todo tipo de información que no sea la clase en sí (varias referencias de símbolo en el grupo constante). En general, se trata de si la clase falta o tiene prohibido acceder a parte de ella. como clases externas, campos de método, etc.
Por ejemplo,
si el nombre completo descrito por la cadena de caracteres en la referencia del símbolo puede encontrar la
clase correspondiente . ¿Hay
un satélite de sala en la clase especificada que cumpla con el método descrito por el descriptor de campo del método y el nombre simple y el método de campo de clase? en el campo símbolo de referencia? Se puede acceder mediante la clase actual

La verificación de la referencia de símbolo es principalmente para asegurar la ejecución normal de la fase de análisis.

Aunque la fase de verificación es importante, no es indispensable.

Listo

La fase de preparación consiste en asignar valores iniciales a esas variables estáticas. Si es una variable estática no final, se le asignará un valor de 0 del tipo correspondiente, si es un tipo final, se asignará directamente al valor ingresado en el código.

Analizando

La etapa de análisis es un proceso en el que la máquina virtual Java reemplaza las referencias de símbolos en el grupo constante con referencias directas.
Referencia de símbolo: una referencia de símbolo es un conjunto de símbolos para describir el objetivo al que se hace referencia. El símbolo puede ser cualquier forma de literal. Siempre que el objetivo se pueda ubicar de forma inequívoca cuando se usa, se puede hacer
referencia directamente: una referencia directa es un puntero que puede apuntar directamente al objetivo. El desplazamiento relativo, o un identificador que se puede ubicar indirectamente al objetivo. Si hay una referencia directa, el destino utilizado por la abuela ya debe existir en la memoria de la máquina virtual.

inicialización

La fase de inicialización es el último paso del proceso de carga de clases. Durante la fase de preparación, a las variables estáticas se les ha asignado un valor de 0. En la fase de inicialización, las variables de clase y otros recursos se inicializan de acuerdo con el plan subjetivo especificado por el programador a través de la codificación del programa. También se puede decir que la fase de inicialización es el proceso de ejecución del método (). Este método se genera automáticamente en el momento de la compilación. Recopilará todas las variables estáticas de la clase para copiar acciones y bloques de declaraciones estáticas.

Cargador de clases

Aunque el cargador de clases solo se usa para implementar la acción de carga de la clase, su función en el programa java supera con creces la etapa de carga. Para cualquier clase, el cargador de clases que lo carga y la propia clase deben establecer conjuntamente la unicidad en otras máquinas virtuales Java. Por ejemplo, solo la misma clase cargada por el mismo cargador de clases es igual. Incluso si dos clases provienen del mismo archivo clss, si sus cargadores de clases son diferentes, entonces no quieren esperar.

Mecanismo de delegación de los padres

Inserte la descripción de la imagen aquí
El proceso de trabajo del modelo de delegación padre es: si un cargador de clases recibe una solicitud de carga de clases, no intentará cargar la clase por sí mismo primero, sino que delega la solicitud a su cargador de clases padre para que la complete, en cada nivel. el caso de todos los cargadores de clases. Por lo tanto, todas las solicitudes de carga de clases deben transmitirse eventualmente al cargador de clases de inicio de nivel superior. Solo cuando el cargador de clases principal informa que no puede completar la solicitud de carga (la clase requerida no se encuentra en su rango de búsqueda), está en su por derecho propio. Intentará terminar de cargar usted mismo.
Ventajas: Las clases en java tienen una relación jerárquica con prioridad junto con su cargador de clases. Por ejemplo, java.lang.Object existe en rt.jar, no importa qué cargador de clases quiera cargar estos tipos, finalmente se delega al cargador de clases de inicio en la parte superior del modelo para cargar. Por lo tanto, se usa la clase Object en todo tipo de programas El entorno del cargador de clases está garantizado para ser la misma clase. Si no se utiliza el mecanismo de delegación padre, cada cargador de clases lo cargará por sí mismo. Si el usuario escribe una clase llamada java.lang.Object y la coloca en el directorio ClassPath, habrá múltiples clases Object sin usar en el sistema.

Supongo que te gusta

Origin blog.csdn.net/qq_30033509/article/details/115004222
Recomendado
Clasificación