Explicación detallada del montón, la pila, el área de métodos y el cargador de clases de la máquina virtual java (JVM)

1. La estructura básica de JVM

diagrama de estructura básica jvm
En la figura anterior, podemos ver que la estructura básica de JVM incluye: subsistema de carga de clases, área de memoria, motor de ejecución e interfaz de biblioteca local.

2. Diagrama detallado de cada área de JVM

Inserte la descripción de la imagen aquí
A través de esta figura, tendremos una comprensión básica de jvm, expliquemos cada área en detalle.

3. Explicación detallada de cada área del área de datos durante el tiempo de ejecución de JVM

3.1 Registro de contador de programa (PC)

El espacio de memoria es pequeño y el hilo es privado. El trabajo del intérprete de código de bytes es seleccionar la siguiente instrucción de código de bytes que debe ejecutarse cambiando el valor de este contador. Las funciones básicas como rama, bucle, salto, manejo de excepciones y recuperación de subprocesos deben ser completadas por el contador.

Si el hilo está ejecutando un método Java, este contador registra la dirección de la instrucción de código de bytes de la máquina virtual que se está ejecutando; si está ejecutando un método nativo, el valor de este contador es (Indefinido). Esta área de memoria es la única área que no especifica ninguna condición de OutOfMemoryError en la especificación de la máquina virtual Java.

3.2 Pilas de máquinas virtuales Java

El hilo es privado y el ciclo de vida es el mismo que el del hilo. Describe el modelo de memoria de la ejecución del método Java: cada método crea un marco de pila cuando se ejecuta para almacenar información como la tabla de variables locales, la pila de operandos, el enlace dinámico, la salida del método, etc. Cada método desde la llamada hasta el final de la ejecución corresponde al proceso de un marco de pila desde la pila de la máquina virtual a la pila.

Tabla de variables locales: almacena varios tipos básicos (booleano, byte, char, short, int, float, long, double), referencias a objetos (tipo de referencia) y tipos returnAddress (apuntando a una instrucción de código byte habla a)

StackOverflowError: la profundidad de la pila solicitada por el hilo es mayor que la profundidad permitida por la máquina virtual.
OutOfMemoryError: si la pila de la máquina virtual se puede expandir dinámicamente y no se puede solicitar suficiente memoria durante la expansión.

3.3 Pilas de métodos nativos

La diferencia con la pila de la máquina virtual Java es que la pila de la máquina virtual Java sirve a la máquina virtual para ejecutar métodos Java (es decir, código de bytes), mientras que la pila de métodos locales sirve a los métodos nativos utilizados por la máquina virtual. También habrá excepciones StackOverflowError y OutOfMemoryError.

3.4 Memoria de pila

Para la mayoría de las aplicaciones, esta área es la mayor parte de la memoria administrada por la JVM. El uso compartido de subprocesos es principalmente para almacenar instancias de objetos y matrices. Los búferes de asignación privada de subprocesos múltiples (búfer de asignación local de subprocesos, TLAB) se dividen internamente. Puede ubicarse en un espacio físicamente discontinuo, pero lógicamente continuo.

OutOfMemoryError: si no hay memoria en el montón para completar la asignación de instancias y el montón ya no se puede expandir, se lanza esta excepción.

3.5 Área de método

Pertenece al área de memoria compartida y almacena datos como información de clase (incluido el nombre de la clase, información del método, información de campo), constantes, variables estáticas y código compilado por el compilador JIT que ha sido cargado por la máquina virtual.

Además de la información de descripción de los campos, métodos e interfaces de la clase en el archivo de clase, también hay un grupo constante, que se utiliza para almacenar las referencias literales y simbólicas generadas durante la compilación.

Una parte muy importante en el área de métodos es el grupo de constantes de tiempo de ejecución, que es la representación en tiempo de ejecución del grupo de constantes de cada clase o interfaz. Una vez que la clase y la interfaz se cargan en la JVM, el grupo de constantes de tiempo de ejecución correspondiente es Creado.

Por supuesto, no es el contenido del grupo de constantes del archivo de clase el que puede ingresar al grupo de constantes de tiempo de ejecución, y también se pueden colocar nuevas constantes en el grupo de constantes de tiempo de ejecución durante el tiempo de ejecución, como el método interno de String. Aunque la especificación de JVM describe el área de método como una parte lógica del montón, tiene el alias non-heap (non-heap).

Aquí hay una imagen para presentar el contenido del área de almacenamiento anterior.
Inserte la descripción de la imagen aquí

4. Mecanismo de carga de clases de máquinas virtuales

La máquina virtual carga los datos que describen la clase desde el archivo Class en la memoria, verifica los datos, analiza e inicializa los datos y finalmente forma un tipo de Java que puede ser utilizado directamente por la máquina virtual.
En el lenguaje Java, la carga, conexión e inicialización de tipos se completan durante la ejecución del programa.

4.1 Ciclo de vida de la clase

Inserte la descripción de la imagen aquí
Se determina la secuencia de las cinco etapas de carga, verificación, preparación, inicialización y descarga. La fase de análisis puede comenzar después de la inicialización (enlace en tiempo de ejecución o enlace dinámico o enlace tardío).

Las siguientes cinco situaciones deben inicializar la clase (y la carga, verificación y preparación, naturalmente, deben completarse antes de esto):

  1. Cuando se encuentran las instrucciones de código de 4 bytes new, getstatic, putstatic o invokestatic, no se activa la inicialización y se activa la inicialización. Escenarios de uso: use la palabra clave new para crear una instancia de un objeto, lea los campos estáticos de una clase (excepto los campos estáticos que han sido modificados por final y coloquen los resultados en el grupo constante en tiempo de compilación) y llame a los métodos estáticos de una clase.
  2. Cuando se usa el método del paquete java.lang.reflect para realizar una llamada de reflexión a la clase.
  3. Al inicializar una clase, si se encuentra que su clase principal aún no se ha inicializado, primero debe activarse la inicialización de su clase principal.
  4. Cuando se inicia la máquina virtual, el usuario debe especificar una clase principal que se cargará (la clase que contiene el método main ()), y la máquina virtual inicializa la clase principal primero.
  5. Cuando se utiliza el soporte de lenguaje dinámico de JDK 1.7, si los identificadores de método de REF_getStatic, REF_putStatic y REF_invokeStatic son los resultados del análisis final de una instancia de java.lang.invoke.MethodHandle, y la clase correspondiente a este identificador de método no se ha inicializado, primero debe activarse Su inicialización.

4.2 proceso de carga de clases

4.2.1 Carga

  1. Obtenga el flujo binario (paquete ZIP, red, generación de operaciones, generación de JSP, lectura de base de datos) que define la subclase a través del nombre completo de una clase.
  2. La estructura de almacenamiento estática representada por este flujo de bytes se transforma en la estructura de datos en tiempo de ejecución del área de métodos.
  3. Un objeto java.lang.Class que representa esta clase se genera en la memoria como un método para acceder a varios datos de esta clase.

La particularidad de la clase de matriz: la clase de matriz en sí no es creada por el cargador de clases, es creada directamente por la máquina virtual Java. Sin embargo, la clase de matriz y el cargador de clases todavía tienen una relación cercana, porque el tipo de elemento de la clase de matriz es creado en última instancia por el cargador de clases. El proceso de creación de la matriz es el siguiente:

  1. Si el tipo de componente de la matriz es un tipo de referencia, use la carga de clases de forma recursiva.
  2. Si el tipo de componente de la matriz no es un tipo de referencia, la máquina virtual Java marcará la matriz como una asociación de cargador de clases de arranque.
  3. La visibilidad de la clase de matriz es consistente con la visibilidad de su tipo de componente. Si el tipo de componente no es un tipo de referencia, la visibilidad de la clase de matriz será pública por defecto.

El objeto java.lang.Class de la instancia en la memoria se almacena en el área de métodos. Como interfaz externa para este tipo de datos en el área de métodos de acceso al programa. Parte del contenido de la fase de carga y la fase de conexión están intercalados, pero el tiempo de inicio permanece en orden.

4.2.2 Verificación

Es el primer paso para garantizar que la información contenida en el flujo de bytes del archivo de clase cumpla con los requisitos de la máquina virtual actual.

Verificación del formato de archivo

  1. Ya sea para comenzar con el número mágico 0xCAFEBABE
  2. Si los números de versión principal y secundaria están dentro del rango de procesamiento de la máquina virtual actual
  3. Si las constantes del grupo de constantes tienen tipos de constantes no admitidos (verifique el indicador de etiqueta constante)
  4. ¿Existe una constante que apunte a una constante inexistente o una constante que no se ajuste al tipo entre los diversos valores de índice que apuntan a la constante
  5. ¿Hay algún dato que no se ajuste a la codificación UTF8 en la constante de tipo CONSTANT_Utf8_info?
  6. Si cada archivo de conjunto de piezas en el archivo de clase tiene otra información adicional eliminada
  7. ……

Solo después de pasar esta etapa de verificación, el flujo de bytes ingresará al área de método de la memoria para su almacenamiento, por lo que las siguientes tres etapas de verificación se basan en la estructura de almacenamiento del área de método, y el flujo de bytes ya no se manipula directamente.

Verificación de metadatos

  1. ¿Esta clase tiene una clase principal (excepto java.lang.Object)?
  2. Si la clase principal de esta clase hereda una clase que no puede heredarse (clase final modificada)
  3. Si esta clase no es una clase abstracta, ¿ha implementado todos los métodos necesarios para ser implementados en su clase principal o interfaz?
  4. Si los campos y métodos de la clase entran en conflicto con la clase principal (anula el campo final de la clase principal y sobrecarga que no se ajusta a la especificación)

Esta etapa es principalmente para realizar una verificación semántica en la información de metadatos de la clase para asegurar que no haya información de metadatos que no se ajuste a la especificación del lenguaje Java.

Verificación de código de bytes

  1. Asegúrese de que el tipo de datos de la pila de operandos y la secuencia del código de instrucción funcionen juntos en cualquier momento (no se leerá un dato de tipo int de acuerdo con el tipo largo)
  2. Asegúrese de que las instrucciones de salto no salten a instrucciones de código de bytes fuera del cuerpo del método
  3. Asegúrese de que la conversión de tipo en el cuerpo del método sea válida (es seguro asignar objetos de subclase al tipo de datos principal y viceversa es ilegal)
  4. ……

Esta es la etapa más complicada de todo el proceso de verificación, cuyo objetivo principal es determinar que la semántica del programa es legal y lógica a través del flujo de datos y el análisis del flujo de control. En esta etapa, el cuerpo del método de la clase se verifica y analiza para garantizar que el método de la clase de verificación no provoque un evento que ponga en peligro la seguridad de la máquina virtual durante la operación.

Verificación de referencia de símbolo

  1. Si el nombre completo descrito por la cadena en la referencia del símbolo puede encontrar la clase correspondiente
  2. Si hay un descriptor de campo del método de símbolo y el método y campo descritos por el nombre simple en la clase especificada
  3. Si la clase actual puede acceder a la accesibilidad (privada, protegida, pública, predeterminada) de la clase, el campo y el método en la referencia de símbolo.
  4. ...... La
    etapa final de verificación ocurre cuando la referencia del símbolo se convierte rápidamente en una referencia directa.Esta acción de conversión ocurrirá en la tercera etapa de la conexión, la fase de análisis. La verificación de referencia de símbolo puede verse como una verificación de coincidencia de información distinta de la clase en sí (varias referencias de símbolo en el grupo constante), así como el contenido mencionado anteriormente.
    El propósito de la referencia de símbolo es garantizar que la acción de análisis se pueda ejecutar normalmente. Si falla la verificación de la referencia de símbolo, se lanzará una subclase de java.lang.IncompatibleClass.ChangeError. Como java.lang.IllegalAccessError, java.lang.NoSuchFieldError, java.lang.NoSuchMethodError, etc.

4.2.3 Preparación

Esta etapa asigna formalmente memoria para la clase y establece el valor inicial de la variable de clase, y la memoria se asigna en el método (las variables con modificación estática no incluyen variables de instancia).

valor int estático público = 1127;

Este código es 0 después de que se establece el valor inicial, porque todavía no se ha ejecutado ningún método Java en este momento. La instrucción putstatic que asigna el valor a 1127 se almacena en el método clinit () después de que se compila el programa, por lo que el valor se asigna durante la fase de inicialización.

El valor cero del tipo de datos básico

Caso especial: si el atributo ConstantValue existe en la tabla de atributos de campo del campo de clase, la máquina virtual asignará el valor a 1127 de acuerdo con la configuración de ConstantValue durante la fase de preparación.

4.2.4 Análisis

En esta etapa, la máquina virtual reemplaza las referencias de símbolos en el grupo constante con referencias directas.

  1. Referencia de símbolo La referencia de
    símbolo es un conjunto de símbolos para describir el objetivo al que se hace referencia y el símbolo puede ser cualquier forma de literal.
  2. Referencia
    directa La referencia directa puede ser un puntero al objetivo, un desplazamiento relativo o un identificador que puede localizar indirectamente el objetivo. La referencia directa está relacionada con la implementación del diseño de memoria de Xunji

La acción de análisis se realiza principalmente para 7 tipos de referencias de símbolos de clases o interfaces, campos, métodos de clase, métodos de interfaz, tipos de métodos, identificadores de métodos y calificadores de puntos de llamada, que corresponden a los 7 tipos de constantes en el grupo de constantes.

4.2.5 Inicialización

El proceso anterior está dominado por la máquina virtual y comienza la fase de inicialización para ejecutar el código Java en la clase.

Cargador de clase 4.3

Obtenga una secuencia de bytes binarios que describa esta clase a través del nombre completo de una clase.

4.3.1 Modelo de delegación principal

Desde la perspectiva de la máquina virtual Java, solo hay dos cargadores de clases: uno es el cargador de clases de inicio (implementado en C ++, que es parte de la máquina virtual); el otro es el cargador para todas las demás clases (implementado en Java, independiente de la máquina virtual) Heredado externa y completamente de java.lang.ClassLoader)

1. Inicie el cargador de clases y
cargue las clases en lib o en -Xbootclasspath

2. El cargador de clases extendido
carga lib / ext o clases en la ruta especificada por la variable de sistema java.ext.dirs

3. El cargador de clases del programa de referencia
ClassLoader es responsable de cargar la biblioteca de clases especificada en la ruta del usuario.

Inserte la descripción de la imagen aquí
A excepción del cargador de clases de inicio de nivel superior, todos los demás tienen su propio cargador de clases padre.
Proceso de trabajo : cuando un cargador de clases determinado recibe una solicitud para cargar una clase, primero delega la tarea de carga al cargador de clases principal y luego de forma recursiva. Si el cargador de clases principal puede completar la tarea de carga de clases, regresa correctamente; si se carga la clase principal Si el dispositivo no puede completar la tarea de carga, lanzará una ClassNotFoundException y luego llamará a su propio método findClass () para cargar, y así sucesivamente.

4.3.2 El papel del modelo de delegación parental

1. Asegúrese de que las clases principales proporcionadas por JVM no se manipulen y garantice la seguridad de la ejecución de la clase.

Por ejemplo, la clase de cadena anterior, sin importar qué cargador desee cargar esta clase, debido al mecanismo de delegación principal, eventualmente será cargada por el cargador de clases de inicio de nivel superior, lo que garantiza que la clase de cadena se encuentre en varios entornos de carga de clases. Son todos de la misma clase. Imagina que si no hay un mecanismo de delegación parental, cada cargador carga la clase de cadena por sí mismo. Es posible que los métodos de cadena cargados por diferentes cargadores de clases sean diferentes. En ese caso, nuestro programa será caótico.

2. Evite la carga repetida de la misma clase

Supongo que te gusta

Origin blog.csdn.net/cyb_123/article/details/108513384
Recomendado
Clasificación