Cómo se almacenan los objetos en el espacio del montón

Tabla de contenido

1. Creación de objetos y asignación de memoria.

2. Diseño de la memoria de objetos

3. Posicionamiento de acceso a objetos


1. Creación de objetos y asignación de memoria.

Cuando la máquina virtual encuentra una nueva instrucción de código de bytes, primero verificará si este tipo se ha cargado. Si no, continúa con el proceso de carga de clases.

Una vez pasada la verificación de carga de clases, la máquina virtual asignará memoria para el nuevo objeto.

El tamaño de memoria requerido para un objeto se puede determinar una vez completada la carga de la clase, por lo que solo necesita dividir una memoria de cierto tamaño del montón.

1. Cómo dividir la memoria del montón

Hay dos formas de gestionar la memoria dinámica de Java:

  • Colisión de punteros: la memoria del montón es absolutamente regular, con datos almacenados en un lado y libres en el otro, separados por un puntero en el medio. Asignar memoria solo requiere mover el puntero
  • Lista libre: la memoria utilizada y la memoria libre se mezclan, y la máquina virtual necesita mantener una lista para registrar qué bloques de memoria están disponibles. Divida de la lista al asignar memoria.

El método de administración de memoria que elige la máquina virtual está relacionado con si el montón de Java es regular o no. Si el montón es regular o no depende de si el recolector de basura utilizado por la máquina virtual tiene la función de "clasificación y compresión de espacio".

  • Cuando se utilizan recopiladores con clasificación y compresión de espacio, como Serial y ParNew, se utiliza la colisión de punteros, que es simple y eficiente.
  • Al utilizar CMS, un recopilador basado en el algoritmo Sweep, en teoría es necesario utilizar una lista libre para asignar memoria.

2. Cómo garantizar la seguridad de la concurrencia dividiendo la memoria

La creación de objetos es una operación muy frecuente en la máquina virtual. Si no se procesa, es probable que la memoria se esté asignando a A pero no se haya completado y B use el estado de memoria original para asignar memoria.

Las máquinas virtuales utilizan dos métodos para garantizar la seguridad de los subprocesos:

  1. Sincroniza la acción de asignar espacio de memoria. (Específicamente, se utiliza el método de bloqueo optimista + reintento fallido)

  2. La acción de asignación de memoria se realiza en diferentes espacios según los subprocesos.

    Cada subproceso 堆中的Eden区preasigna una pequeña porción de memoria como un "búfer de asignación de subprocesos local (TLAB)" y 线程优先在自己的TLAB中分配内存luego la sincroniza cuando no es suficiente.

Si la máquina virtual habilita TLAB se establece mediante este parámetro:

-XX:+/-Usar TLAB

3. Trabajar después de completar la asignación de memoria.

Se asigna la memoria y la máquina virtual procesa el espacio asignado, inicializa todo excepto el encabezado del objeto a valores cero y luego llena el encabezado del objeto.

En este punto, para la máquina virtual, se crea el objeto. Pero este es un objeto en blanco. El constructor en el código Java aún no se ha ejecutado, por lo que el objeto aún no se ha inicializado.

Solo después de ejecutar la nueva instrucción y ejecutar el constructor <init>(), se crea completamente un objeto Java utilizable.

4. Resumir el proceso de creación de objetos Java.

El proceso de creación de objetos Java:

Comprobación de tipos, asignación de memoria, inicialización del valor cero, configuración del encabezado del objeto, ejecución del método constructor

  1. Cuando encuentre la nueva palabra clave, primero verifique si el parámetro de esta instrucción puede encontrar una referencia de símbolo de este tipo en el grupo constante.

  2. Si lo encuentra, verifique si el tipo se ha cargado e inicializado

  3. Si no se encuentra, significa que la clase aún no se ha cargado y el proceso de carga de clases se realiza primero.

  4. Una vez pasada la fase de verificación de carga de clases, se ha determinado el tamaño de memoria que debe ocupar el objeto de esta clase. JVM asignará memoria al objeto.

    • La asignación de memoria implica tres detalles:

      • Dos métodos de asignación de memoria: colisión de punteros y lista libre

      • Seguridad de subprocesos en la asignación de memoria: bloqueo optimista + reintento en caso de falla

      • Para objetos pequeños, el hilo primero asigna memoria en su propio "Thread Local Allocation Buffer TLAB" en el montón.

        Para objetos grandes, puedes optar por colocarlos directamente en la generación anterior.

  5. Procese el espacio de memoria asignado e inicialice todo excepto el encabezado del objeto a valores cero para que las variables miembro del objeto tengan valores predeterminados.

  6. Complete el encabezado del objeto, establezca el tipo de objeto, la edad de generación y si desea habilitar el bloqueo de sesgo. El código hash se carga de forma diferida en la primera llamada.

  7. Ejecutar el método constructor del objeto.

Luego se obtiene un objeto Java utilizable.

5. Estrategias básicas para la asignación de memoria de objetos.

En general:

  1. Los nuevos objetos se asignan primero en el área del Edén.
  2. Los objetos grandes entran directamente en la vieja generación.
  3. Los objetos de larga vida pasarán a la vieja generación.

1. Los nuevos objetos se asignan primero en el área del Edén.

Si no hay suficiente espacio en el área del Edén, se activará un GC menor.

  • Si durante la GC de nueva generación, hay muchos objetos supervivientes en el área de Eden y el área de supervivientes no puede caber en ellos, los objetos de nueva generación se copiarán a la generación anterior a través del mecanismo de garantía de asignación.
  • Si no hay suficiente espacio en la generación anterior, se activará un GC completo, lo que lleva mucho tiempo.

2. ¿Por qué los objetos grandes entran directamente en la vejez?

Los objetos grandes son objetos que requieren una gran cantidad de espacio de memoria continuo (como cadenas y matrices).

Hay dos aspectos a considerar:

  • Tal vez el área del Edén de la nueva generación no tenga suficiente espacio de memoria y tenga que activar un GC por adelantado. Porque los objetos grandes tienen una mayor probabilidad de encontrar memoria insuficiente.
  • En el futuro GC de nueva generación, si el objeto grande sobrevive, es posible que el área de superviviente no pueda acomodarlo y aún ingresará a la generación anterior a través del mecanismo de garantía de asignación. Entonces puedes optar por ponerlo directamente en la generación anterior.

Hay un parámetro:

-XX: Umbral de tamaño de pretenencia

Números mayores que este se asignan directamente a la generación anterior. El valor predeterminado es 0, lo que significa que no se asignará directamente en la generación anterior.

3. Los objetos que sobrevivan durante mucho tiempo pasarán a la vieja generación.

Cada objeto guardará una era generacional, cada vez que sobreviva a un GC, la edad generacional será +1.

Cuando la edad de la generación supere el umbral, será promovida a la generación anterior.

Este parámetro se puede ajustar, el valor predeterminado es 15

-XX: Umbral de tenencia máxima

HotSpot utiliza aquí un mecanismo dinámico de edad generacional, que está documentado en la teoría de la colección generacional.

2. Diseño de la memoria de objetos

En la máquina virtual HotSpot, el diseño de almacenamiento de objetos en la memoria dinámica se puede dividir en tres partes:

  • Encabezado de objeto (encabezado)
  • Datos de instancia
  • Relleno de alineación

1. Encabezado del objeto

La parte del encabezado del objeto contiene dos tipos de información:

  • palabra de marca: se utiliza para almacenar los datos de tiempo de ejecución del objeto en sí, como el valor hash, la edad de generación de CG, el indicador de estado de bloqueo, el bloqueo mantenido por el hilo, la ID del hilo sesgado, la marca de tiempo sesgada, etc.
  • klass word: puntero de tipo, es decir, el puntero del objeto a sus metadatos de tipo. La JVM utiliza este puntero para determinar de qué clase es una instancia el objeto.

Si el objeto es una matriz, el encabezado del objeto también debe almacenar su longitud; de lo contrario, no se puede determinar el tamaño del objeto de la matriz.

2. Datos de instancia

Almacena la información válida del objeto, incluidas las definidas en el código y heredadas de la clase principal. El orden de almacenamiento tiene dos efectos:

  • Orden de escritura en código
  • Estrategia de asignación de máquinas virtuales

La estrategia de asignación predeterminada es que los campos del mismo ancho se asignarán y almacenarán juntos. Según esta condición, las variables de la clase padre precederán a las de la subclase.

Si HotSpot activa este parámetro, se pueden insertar variables más estrechas en la subclase en los espacios de las variables de la clase principal, ahorrando un poco de espacio.

+XX:CompactFields:true //El valor predeterminado es verdadero

3. Alineación y llenado

Esta parte actúa como marcador de posición.

El sistema de administración automática de memoria de la máquina virtual HotSpot requiere que la dirección inicial del objeto sea un múltiplo entero de 8 bytes, por lo que el tamaño de cualquier objeto debe ser un múltiplo entero de 8 bytes.

La parte del encabezado del objeto se ha diseñado cuidadosamente para tener 8 bytes o 16 bytes, pero no se puede garantizar la longitud de la parte de datos de la instancia. Si tiene menos de 8 bytes, simplemente use el relleno de alineación para compensarlo.

3. Posicionamiento de acceso a objetos

El método de posicionamiento de acceso de un objeto se refiere a cómo la referencia en la pila apunta al objeto en el montón.

En los programas Java 通过栈上的 reference 数据来操作堆上的具体对象, este tipo de referencia solo se fija como referencia sin especificar el método de implementación.

Por lo tanto, la máquina virtual puede implementar libremente el método de acceso a objetos. Hay dos métodos principales:

  • Utilice el acceso por manija:
    • Una parte de la memoria se divide en el montón de Java como un grupo de identificadores.
    • La dirección del identificador del objeto almacenado en referencia.
    • El identificador contiene la información de dirección específica de los "datos de instancia" y los "datos de tipo" del objeto.
  • Utilice el acceso directo al puntero:
    • La dirección específica del objeto se almacena directamente en referencia.

Ambos métodos tienen sus propias ventajas:

  • La mayor ventaja del acceso al identificador es que la referencia almacena una dirección de identificador estable. Cuando el objeto se mueve (como ocurre con la recolección de basura), solo se cambiará el puntero de datos de la instancia en el identificador, sin modificar la referencia.
  • La ventaja del acceso directo al puntero es que es más rápido y ahorra la sobrecarga del posicionamiento del puntero. Las operaciones de acceso a objetos son muy frecuentes, que también es la solución utilizada por HotSpot.

El método de posicionamiento de acceso a objetos específico está relacionado con el tipo de GC.

Supongo que te gusta

Origin blog.csdn.net/m0_62609939/article/details/130602389
Recomendado
Clasificación