JVM --- Diseño de memoria instanciado y ubicación de acceso de objetos

Disposición de la memoria de instanciación de objetos y ubicación de acceso

Preguntas de entrevista

  1. ¿Cómo se almacenan los objetos en la JVM?
  2. ¿Qué hay en la información del encabezado del objeto?
  3. ¿Qué hay en el encabezado del objeto Java?

Comience con el método y los pasos de la creación de objetos:
Inserte la descripción de la imagen aquí

La forma en que se crea el objeto

  • nuevo: la forma más común de llamar al método de clase estática de getInstance en la clase singleton, el método estático de XXXFactory;
  • Método newInstance de la clase: un método marcado como obsoleto en JDK9, porque solo puede llamar al constructor de parámetros vacío;
  • NewInstance (XXX) del constructor: método de reflexión, puede llamar al constructor con parámetros vacíos o;
  • Use clone (): no llame a ningún constructor y requiera que la clase actual implemente la interfaz clon en la interfaz Cloneable;
  • Usar serialización: la serialización se usa generalmente para la transmisión de la red Socket;
  • Objenesis, una biblioteca de terceros.

Pasos para la creación de objetos

Mira el proceso de creación del objeto a partir del código de bytes:

Código de muestra:

public class ObjectTest {
    
    
    public static void main(String[] args) {
    
    
        Object obj = new Object();
    }
}

Bytecode:

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class java/lang/Object
         3: dup           
         4: invokespecial #1                  // Method java/lang/Object."<init>":()V
         7: astore_1
         8: return
      LineNumberTable:
        line 9: 0
        line 10: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
            8       1     1   obj   Ljava/lang/Object;
}

1. Determine si la clase correspondiente al objeto está cargada, vinculada e inicializada

  1. Cuando la máquina virtual encuentra una nueva instrucción, primero verifique si los parámetros de esta instrucción pueden ubicar una referencia simbólica de una clase en el grupo constante de Metaspace (área de método), y verifique si la clase representada por esta referencia simbólica se ha cargado. analizado e inicializado. (Es decir, para determinar si existe la metainformación);
  2. Si la clase no está cargada, en el modo de delegación principal, use el cargador de clases actual para encontrar el archivo .class correspondiente con ClassLoader + nombre de paquete + clave de nombre de clase. Si no se encuentra el archivo, se lanzará ClassNotFoundException. Si se encuentra , Se carga la clase y se genera el objeto Class correspondiente.

2. Asignar memoria para el objeto

  1. Primero calcule el tamaño del espacio ocupado por el objeto y luego divida un bloque de memoria en el montón para el nuevo objeto. Si la variable miembro de instancia es una variable de referencia, simplemente asigne el espacio de la variable de referencia, que tiene un tamaño de 4 bytes;
  2. Si la memoria es normal: use la colisión de punteros para asignar memoria.
    • Si la memoria es regular, la máquina virtual utilizará el método Bump The Point para asignar memoria para el objeto. Significa que toda la memoria utilizada está en un lado, la memoria libre se almacena en el otro lado y un puntero se coloca en el medio como un indicador del punto de división. Asignar memoria es simplemente mover el puntero a la memoria libre por una distancia igual al tamaño del objeto.
    • Si el recolector de basura elige Serial, ParNew, que se basa en el algoritmo de compresión, la máquina virtual usa este método de asignación. Generalmente, las colisiones de punteros se usan cuando se usa el recolector con el proceso Compacto.
    • El algoritmo de compresión de marcas (desfragmentación) desfragmentará la memoria. Un lado de la memoria del montón almacena objetos y el otro lado es un área libre.
  3. Si la memoria no es regular: lista libre.
    • Si la memoria no es regular, la memoria utilizada y la memoria no utilizada se intercalan entre sí, entonces la máquina virtual usará la lista libre para asignar memoria para el objeto. Significa que la máquina virtual mantiene una lista para registrar qué bloques de memoria están disponibles. Al redistribuir, busque un espacio lo suficientemente grande en la lista para dividir la instancia del objeto y actualizar el contenido de la lista. Este método de asignación se convirtió en la "Lista libre (Lista libre)".
    • La elección del método de asignación está determinada por si el montón de Java es regular o no, y si el montón de Java es regular o no, está determinado por si el recolector de basura utilizado tiene una función de compresión.
    • Una vez que el algoritmo de barrido de marcas limpia la memoria del montón, habrá muchos fragmentos de memoria.

3. Abordar los problemas de simultaneidad

  1. Utilice el reintento de falla CAS + para garantizar la atomicidad de la actualización;
  2. Asignar previamente TLAB para cada conjunto de subprocesos configurando el parámetro -XX: + UseTLAB (mecanismo de bloqueo de área);
  3. Asigne un área para cada hilo en el área de Edén.

4. Inicialice el espacio asignado

  1. Todas las propiedades se establecen en valores predeterminados para garantizar que los campos de instancia de objeto se puedan utilizar directamente sin asignar valores.
  2. El orden de asignación de valores a las propiedades del objeto:
    • Inicialización del valor predeterminado de la propiedad
    • Visualización de inicialización / inicialización de bloque de código (relación paralela, quién es el primero y quién verá el orden de escritura del código)
    • Inicializador

5. Establecer el encabezado del objeto del objeto.

  • Almacene la clase del objeto (es decir, la información de metadatos de la clase), el HashCode del objeto, la
    información de GC del objeto, la información de bloqueo y otros datos en el encabezado del objeto del objeto. La configuración específica de este proceso depende de la implementación de JVM.

6. Ejecute el método init para inicializar.

  1. Desde la perspectiva de un programa Java, la inicialización solo ha comenzado oficialmente. Inicialice las variables miembro, ejecute el bloque de código instanciado, llame al método constructor de la clase y asigne la primera dirección del objeto en el montón a la variable de referencia;
  2. Entonces, en general (determinado por la instrucción especial de invocación en el código de bytes), el método init se ejecutará después de la nueva instrucción para inicializar el objeto de acuerdo con los deseos del programador, de modo que se considere que se ha creado un objeto realmente utilizable.

Mire el método init desde una perspectiva de código de bytes

/**
 * 测试对象实例化的过程
 *  ① 加载类元信息 - ② 为对象分配内存 - ③ 处理并发问题  - ④ 属性的默认初始化(零值初始化)
 *  - ⑤ 设置对象头的信息 - ⑥ 属性的显式初始化、代码块中初始化、构造器中初始化
 *
 *
 *  给对象的属性赋值的操作:
 *  ① 属性的默认初始化 - ② 显式初始化 / ③ 代码块中初始化 - ④ 构造器中初始化
 */

public class Customer{
    
    
    int id = 1001;
    String name;
    Account acct;

    {
    
    
        name = "匿名客户";
    }
    public Customer(){
    
    
        acct = new Account();
    }

}
class Account{
    
    

}

Información de código de bytes:

 0 aload_0
 1 invokespecial #1 <java/lang/Object.<init>>
 4 aload_0
 5 sipush 1001
 8 putfield #2 <com/atguigu/java/Customer.id>
11 aload_0
12 ldc #3 <匿名客户>
14 putfield #4 <com/atguigu/java/Customer.name>
17 aload_0
18 new #5 <com/atguigu/java/Account>
21 dup
22 invokespecial #6 <com/atguigu/java/Account.<init>>
25 putfield #7 <com/atguigu/java/Customer.acct>
28 return

Las instrucciones de código de bytes del método init ():

  • El valor predeterminado del atributo se inicializa: id = 1001;
  • Visualización de inicialización / inicialización de bloque de código: nombre = "Cliente anónimo";
  • Inicialización del constructor: acct = new Account ();

El diseño de memoria del objeto
Inserte la descripción de la imagen aquí
Nota: lo que apunta el puntero de tipo es en realidad la metainformación almacenada en el área de método.

Resumen de la distribución de la memoria

Inserte la descripción de la imagen aquí

Ubicación de acceso a objetos

¿Cómo accede la JVM a sus instancias de objetos internos a través de las referencias de objetos en el marco de pila?
Inserte la descripción de la imagen aquí
Posicionamiento, accesible por referencia en la pila

Dos formas de acceder a los objetos: manejar el acceso y el puntero directo:

1. Manejar el acceso

  • Desventajas: se abre un espacio en el espacio del montón como el grupo de controles, y el grupo de controles en sí también ocupará espacio; solo se puede acceder a los objetos del montón a través de dos accesos de puntero, lo cual es ineficiente.
  • Ventajas: la referencia almacena la dirección del identificador estable. Cuando se mueve el objeto (es común mover el objeto durante la recolección de basura), solo se puede cambiar el puntero de datos de instancia en el identificador y no es necesario modificar la referencia en sí .
    Inserte la descripción de la imagen aquí

2. Puntero directo (adoptado por HotSpot)

  • Características: el puntero directo es una referencia en la tabla de variables locales, que apunta directamente a la instancia en el montón, y hay un puntero de tipo en la instancia del objeto, que apunta a los datos del tipo de objeto en el área de método.
  • Desventajas: El valor de la referencia debe modificarse cuando se mueve el objeto (es común mover objetos durante la recolección de basura).
    Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_33626996/article/details/114652416
Recomendado
Clasificación