[JVM] (1) Comprensión profunda del área de datos del tiempo de ejecución de JVM


1. Proceso de ejecución de JVM

JVM es la base operativa y el entorno operativo de los programas Java, y también es "一次编译,到处运行"la clave para la implementación de Java. Por lo tanto, una comprensión profunda de la JVM es crucial para aprender y comprender el lenguaje de programación Java, pero ¿cómo funciona exactamente la JVM?

La siguiente imagen muestra el proceso operativo básico de JVM:

El proceso de ejecución de la JVM involucra los siguientes componentes principales:

  1. Convertir código Java en código de bytes ( .classarchivo) : después de escribir, un compilador de Java debe compilar el código fuente de Java en código de bytes para generar .classarchivos, que contienen el código intermedio del programa Java.

  2. Cargador de clases (ClassLoader) : el cargador de clases de JVM es responsable de .classcargar el archivo de código de bytes en la memoria 运行时数据区. El cargador de clases busca y carga el archivo de código de bytes correspondiente de acuerdo con el nombre completo (nombre completo) de la clase y crea un objeto de clase de acuerdo con el contenido del archivo para representar esta clase para llamadas posteriores al motor de ejecución .

  3. Gestión de memoria en ejecución : JVM es responsable de gestionar el área de memoria cuando el programa se está ejecutando, e incluye principalmente las siguientes áreas:

    • Pilas de máquinas virtuales (pilas de máquinas virtuales Java) : marcos de pila utilizados para almacenar llamadas a métodos, incluidas variables locales, pilas de operandos, etc.
    • Pilas de métodos nativos : pilas utilizadas para ejecutar métodos nativos.
    • Área de método : almacena información estructural de la clase, grupo constante, variables estáticas, etc.
    • Montón (Montón) : el área de memoria que almacena instancias de objetos y matrices.
    • Contador de programa (Contador de programa) : registra la dirección o índice de la instrucción de código de bytes ejecutada por el hilo actual.
  4. Motor de ejecución : el motor de ejecución es uno de los componentes principales de la JVM y es responsable de ejecutar archivos de código de bytes cargados en la memoria . El motor de ejecución ejecuta el código de bytes de dos maneras:

    • Interpretación y ejecución : Interpreta las instrucciones de código de bytes una por una y realiza las operaciones correspondientes . La interpretación y ejecución son menos eficientes, pero multiplataforma, y ​​son adecuadas para segmentos de código que acaban de comenzar a ejecutarse o segmentos de código que se ejecutan con menos frecuencia.
    • Compilar y ejecutar : Compile el código de bytes en código nativo para una plataforma específica y luego entréguelo a la CPU para su ejecución . La eficiencia de compilación y ejecución es alta, pero requiere tiempo de compilación adicional y es adecuada para segmentos de código con tiempos de ejecución frecuentes.
  5. Bibliotecas nativas : el código Java en la JVM no puede acceder directamente al sistema operativo subyacente, porque el código de bytes es solo un conjunto de especificaciones de conjunto de instrucciones multiplataforma. Cuando el código Java necesita ejecutar el sistema operativo subyacente o interactuar con código nativo (como C, C++). Estas funciones deben realizarse a través de la biblioteca de métodos local . La biblioteca de métodos nativos permite a los programas Java llamar a códigos nativos relacionados con el sistema operativo, realizando así las funciones de todo el programa.

Lo anterior es el proceso de ejecución principal de la JVM y los componentes involucrados en el tiempo de ejecución.下文是对其中的运行时数据区的详细介绍。

2. Pila de máquinas virtuales (hilo privado)

La pila de máquinas virtuales es un área de memoria privada para los subprocesos de Java, que se utiliza para almacenar información como llamadas a métodos de subprocesos y variables locales . El ciclo de vida de la pila de máquinas virtuales es el mismo que el de los subprocesos. La pila de máquinas virtuales describe el modelo de memoria de ejecución del método Java:

  • Cuando se ejecuta cada método, se crea un marco de pila (Stack Frame) para almacenar la tabla de variables locales, la pila de operandos, el enlace dinámico, la salida del método y otra información.
  • Cuando finaliza el método, el marco de la pila correspondiente también se destruirá al hacer estallar la pila.

La pila de máquinas virtuales consta principalmente de cuatro partes:

  1. Tabla de variables locales : utilizada para almacenar las variables locales del método . Cuando se ejecuta un método Java, asigna un área de memoria para almacenar los parámetros del método y las variables locales definidas dentro del método. El tamaño de la tabla de variables locales se determina durante la compilación y los tipos de datos almacenados incluyen tipos de datos básicos y referencias de objetos.
  2. Pila de operaciones : se utiliza para calcular operaciones al ejecutar métodos . Las instrucciones de código de bytes de la máquina virtual Java generalmente se basan en la pila de operandos. Cuando se llama a un método, el valor del parámetro y el valor de retorno se enviarán a la pila de operandos. Cuando se ejecuta el método, la instrucción de código de bytes recuperará el valor de la pila de operandos para su cálculo.
  3. Enlace dinámico : se utiliza para señalar la referencia al método al que pertenece el marco de pila en el grupo constante de tiempo de ejecución . En el grupo constante de tiempo de ejecución de la máquina virtual Java, se almacenan las referencias simbólicas de los métodos de cada clase y el enlace dinámico asocia estas referencias simbólicas con las direcciones de memoria reales.
  4. Dirección de retorno del método : se utiliza para indicar la dirección de retorno del método . Cuando se completa la ejecución del método, es necesario saber dónde continuar la ejecución. La dirección de retorno del método se utiliza para registrar la dirección de destino de retorno.

¿Qué es el hilo privado?

  • Subproceso privado significa que en un entorno de subprocesos múltiples, cada subproceso tiene su propia área de memoria independiente y otros subprocesos no pueden acceder ni modificar directamente esta área. Los datos en la memoria privada del hilo son independientes para cada hilo y no se afectan entre sí. Este diseño permite que programas multiproceso se ejecuten simultáneamente, con cada subproceso ejecutando y manteniendo sus propios datos de forma independiente.
  • En la JVM, 虚拟机栈y 本地方法栈son 程序计数器áreas de memoria privadas de subprocesos . Cada subproceso tiene su propia pila de máquinas virtuales y su propia pila de métodos locales para admitir la invocación y ejecución de métodos, y los datos de estas pilas son invisibles para otros subprocesos. El contador de programa también es privado de subprocesos y cada subproceso tiene su propio contador de programa, que se utiliza para indicar la dirección o índice de la instrucción de código de bytes ejecutada por el subproceso actual. Cuando se cambia el hilo, el valor del contador del programa se guardará y restaurará para garantizar que el hilo pueda continuar ejecutándose correctamente después de cambiar el hilo.
  • Otras áreas de memoria compartida, como y, 方法区(元空间)son compartidas por subprocesos. El montón se usa para almacenar matrices y instancias de objetos Java, y el área de métodos se usa para almacenar información de estructura de clases, grupos constantes, variables estáticas, etc. Varios subprocesos pueden acceder conjuntamente a los datos en el montón y en el área de método, por lo que en un entorno de subprocesos múltiples, se requiere un mecanismo de sincronización para proteger la coherencia y corrección de los datos compartidos.
  • El área de memoria se divide en subprocesos privados y compartidos:

3. Pila de métodos locales (hilo privado)

La pila de métodos nativos es similar a la pila de máquinas virtuales y también es un área de memoria privada para los subprocesos de Java. Se utiliza para admitir programas Java para llamar y ejecutar métodos nativos (Método nativo) . Un método nativo es un método escrito en otros lenguajes (como C, C++) e interactúa con el código Java a través de una interfaz de método nativo (JNI, Java Native Interface). La pila de métodos nativos y la pila de máquinas virtuales también tienen funciones similares, pero se utilizan para llamar a métodos Java y métodos nativos respectivamente.

4. Área de método (área de metadatos)

El área de método es un área de memoria compartida por subprocesos de Java , que se utiliza para almacenar información estructural de clases, grupos constantes, variables estáticas, código compilado por el compilador justo a tiempo, etc. En JDK 8 y versiones anteriores, el área de método es 永久代(Permanent Generation); mientras que en JDK 8 y versiones posteriores, el área de método se reemplaza por 元空间(Metaspace). El tamaño del área del método se puede establecer mediante parámetros al iniciar la JVM.

En la "Especificación de la máquina virtual Java", esta área se denomina "área de método", y en la implementación de la máquina virtual HotSpot, esta área se denomina Generación permanente (Generación permanente) en JDK 7 y Metaspace (Metaspace) en JDK 8). .

Cambios en el metaespacio JDK 1.8:

  1. Para HotSpot, la memoria del metaespacio JDK 8 pertenece a la memoria local, por lo que el tamaño del metaespacio ya no se ve afectado por los parámetros de la memoria máxima de la JVM, sino que está relacionado con el tamaño de la memoria local.
  2. En JDK 8, el grupo de constantes de cadena se movió al montón.

Grupo constante de tiempo de ejecución:

El grupo de constantes de tiempo de ejecución (Runtime Constant Pool) es parte del área de métodos y se utiliza para almacenar varias referencias literales y simbólicas generadas durante la compilación. La máquina virtual lo construye de acuerdo con la tabla de grupo constante en el archivo de código de bytes durante el proceso de carga de clases.

En el grupo constante de tiempo de ejecución, existen principalmente dos tipos de datos:

  1. literales :

    • Literal de cadena: el valor de cadena escrito directamente en el programa Java, por ejemplo: "Hola, Java". En JDK 8, los literales de cadena se mueven al montón para que también puedan recolectarse como basura, evitando algunos problemas de memoria.
    • finalConstantes: Constantes que se pueden determinar durante la compilación final, por ejemplo final int MAX_VALUE = 100;:.
    • Valores de tipos de datos básicos: por ejemplo: literales de tipos de datos básicos como números enteros, números de punto flotante y caracteres.
  2. Referencia simbólica :
    una referencia simbólica es una estructura de datos que se genera durante la compilación pero que debe usarse durante la fase de carga de clases para describir el objetivo al que se hace referencia. Las referencias simbólicas incluyen:

    • Nombres completos de clases e interfaces: Por ejemplo: java.lang.String.
    • Nombre de campo y descriptor: se utiliza para describir el nombre y el tipo de datos del campo, por ejemplo: int count.
    • Nombre y descriptor del método: se utiliza para describir el nombre del método, la lista de parámetros y el tipo de valor de retorno, por ejemplo: void print(String message).

El grupo de constantes de tiempo de ejecución se almacena en el área de métodos después de cargar la clase, existe durante todo el ciclo de vida de la clase y se recicla junto con la clase misma. Proporciona funciones como análisis de grupo constante, enlaces dinámicos e invocación de métodos cuando el programa se está ejecutando, brindando el soporte necesario para la ejecución de programas Java.

5. Montón (compartir hilos)

El montón (Heap) es un área de datos en tiempo de ejecución en la máquina virtual Java para almacenar matrices e instancias de objetos Java . El montón es el área de memoria más grande en la JVM y la única área de memoria compartida por todos los subprocesos.

Cuando se ejecuta un programa Java, cuando newse utiliza una palabra clave para crear un objeto, la instancia del objeto se asignará en el montón. Al mismo tiempo, las matrices también son objetos, por lo que los elementos de la matriz también se almacenan en el montón. El tamaño del montón se puede especificar mediante parámetros al iniciar la JVM, o se puede ajustar dinámicamente (si no se especifica el tamaño, la JVM establecerá automáticamente el tamaño inicial de acuerdo con la memoria del sistema).

Las características clave del montón incluyen:

  1. Compartir subprocesos : el montón es un área de memoria compartida por todos los subprocesos. Todos los subprocesos pueden acceder a instancias de objetos en el montón, lo que permite que varios subprocesos manipulen y compartan objetos de forma conjunta.

  2. Asignación y reciclaje dinámicos : el tamaño del montón se puede ajustar dinámicamente cuando el programa se está ejecutando. Cuando se crea un objeto, la JVM asigna automáticamente espacio de memoria en el montón. El recolector de basura recupera automáticamente la memoria de los objetos no utilizados en el montón cuando ya no se hace referencia a dichos objetos.

  3. Recolección de basura : la memoria en el montón es administrada por el recolector de basura. El recolector de basura verifica periódicamente los objetos en el montón, marca los objetos a los que ya no se hace referencia como basura y luego recupera la memoria de estos objetos basura y los libera al montón para que otros objetos los utilicen.

  4. Gestión automática de la memoria : la JVM gestiona automáticamente la memoria del montón en Java y el programador no necesita liberar la memoria manualmente. La JVM realizará automáticamente la recolección de basura para liberar la memoria no utilizada, evitando problemas como pérdidas de memoria.

Además, en la memoria dinámica de la máquina virtual Java, generalmente se divide en dos áreas principales: 新生代(Young Generation)y老生代(Old Generation) .

  1. Generación Joven :
    La Generación Joven es un área donde se almacenan los objetos recién creados . En la nueva generación, la memoria del montón generalmente se divide en un espacio Eden y dos espacios Survivor (generalmente llamados S0 y S1). Los objetos recién creados se asignan primero al espacio del Edén. Cuando el espacio de Eden esté lleno, se activará Minor GC (Young GC) y el recolector de basura copiará el espacio de Eden y los objetos supervivientes que contiene en un espacio de Survivor no utilizado. Luego, el recolector de basura limpiará el espacio Eden y el espacio Survivor en uso, y reciclará los objetos basura que contenga. Los objetos supervivientes se promocionan a la generación anterior.

  2. Old Generation (Vieja Generación) :
    La Vieja Generación es un área que almacena objetos de larga duración . Los objetos almacenados en la generación anterior suelen ser objetos que sobreviven después de una cierta cantidad de GC menores en la nueva generación u objetos grandes. Cuando el espacio de la generación anterior esté lleno, se activará Major GC (Full GC) y el recolector de basura reciclará todo el montón, incluidos todos los objetos de la nueva generación y de la antigua generación.

Al dividir la memoria del montón en la nueva generación y la antigua generación, y adoptar diferentes estrategias de recolección de basura, se puede mejorar la eficiencia de la recolección de basura. El GC menor en la generación joven se realiza con frecuencia para reciclar objetos con un ciclo de vida corto y liberar memoria lo más rápido posible, mientras que el GC mayor en la generación anterior es relativamente poco frecuente para reciclar objetos con un ciclo de vida largo para garantizar la estabilidad de la vieja generación y confiabilidad.

6. Contador de programa (hilo privado)

El contador del programa también es un área de memoria privada para los subprocesos de Java. Es un indicador que indica qué está ejecutando el hilo actual字节码指令的地址或索引 . Cuando se cambia el hilo de Java, el valor del contador del programa se guardará y restaurará para garantizar que el hilo pueda continuar ejecutándose correctamente después de cambiar el hilo .

Supongo que te gusta

Origin blog.csdn.net/qq_61635026/article/details/132028246
Recomendado
Clasificación