Tamaños de diferentes tipos de objetos Java

Introduccion


¿El objeto Java contiene:

  • campos declarados en superclase?
  • campos privados declarados en una superclase?
  • métodos?
  • elementos de la matriz?
  • longitud de la matriz?
  • otro objeto (en sí mismo)?
  • código hash?
  • tipo (propio)?
  • nombre (propio)?

Las respuestas a estas (y otras) preguntas se pueden obtener utilizando la biblioteca de clases org.openjdk.jol que, en particular, nos permite comprender que el objeto es un área de memoria:

  • que contiene:
    • encabezado (hasta 16 bytes), y en él:
      • código hash
      • referencia de tipo
      • longitud de matriz (para matriz)
    • todos los campos (incluidos los privados) declarados en todas las superclases
    • o elementos de matriz (para una matriz)
  • sin contener:
    • variables estáticas
    • métodos
    • otros objetos en ti
    • nombre propio (es decir, el objeto no tiene nombre)


Preparación


Aquí están los resultados de evaluar la memoria de objetos de varios tipos mediante el método de la descripción del paquete java.lang.instrument (ver también aquí ). Estos resultados nos permiten responder a la mayoría de las preguntas planteadas anteriormente.

Deben completarse los siguientes pasos:

  1. Cree una clase de agente que contenga el método principal:
    public static void premain(String, Instrumentation) {...} 
  2. Cree un archivo que contenga la clase de agente y el archivo de manifiesto con el contenido:
     Premain-class: -- 
  3. Cree una clase ejecutable para evaluar la memoria.
  4. Especifique el archivo con el parámetro "-javaagent" al iniciar la máquina virtual:
     java -javaagent:- -- 


Comencemos con un caso de prueba. Para simplificar, usamos un paquete sin nombre.

Paso 1. Crear una clase de agente de prueba


 import java.lang.instrument.Instrumentation; public class A { public static void premain(String notUsedHere, Instrumentation i) { System.out.println("premain"); } } 

Compilamos:

 javac A.java 

Paso 2. Cree un archivo de manifiesto m.txt que contenga:


 Premain-class: A   

ATENCIÓN: la segunda línea del archivo debe estar VACÍA, SIN CONTENER ESPACIOS.

Crea el archivo A.jar:

 jar cmf m.txt A.jar A.class 

Paso 3. Crea una clase ejecutable de prueba


 public class M { public static void main(String[] notUsedHere) { //     System.out.println("main"); } } 

Compilamos:

 javac M.java 

Paso 4. Realizar


 java -javaagent:A.jar M 

Resultado:
 premain main 

indica que primero se llamó al método principal de la clase de agente y luego al método principal de la clase ejecutable.

Ahora cree la clase de agente requerida:

 import java.lang.instrument.Instrumentation; public class A { //      private static Instrumentation ins; public static void premain(String notUsedHere, Instrumentation i) { ins = i; } public static Instrumentation instrumentation() {return ins;} } 

y clase ejecutable:

 class M { public static void main(String[] notUsedHere) { mem("Object", new Object()); } private static void mem(Object o, Object ref) { System.out.println(o + ": " + objectBytesEstimate(ref)); } private static long objectBytesEstimate(Object ref) { if (A.instrumentation() == null) { throw new RuntimeException("Not initialized instrumentation."); } return A.instrumentation().getObjectSize(ref); } } 

Método

 long getObjectSize(Object --) 

devuelve una ESTIMACIÓN del tamaño (número de bytes) de memoria ocupada por el objeto en el enlace especificado. Debe tenerse en cuenta que la estimación resultante puede ser diferente para otra máquina virtual. Los valores para jdk-13 se enumerarán aquí .

Realizamos:

 javac *.java jar cmf m.txt A.jar A.class java -javaagent:A.jar M 

y obtenemos el resultado:

 Object: 16 

mostrando que un objeto VACÍO de tipo Objeto ocupa aquí (POR EVALUACIÓN) 16 bytes. De estos, 12 bytes ocupan el encabezado, y 4 bytes al final sirven para alinear la longitud del objeto al límite de 8 bytes.

Resultados


Otros ejemplos contendrán solo el código colocado en el método principal de la clase M. Deben ejecutarse para cada ejemplo con los comandos:

 javac M.java java -javaagent:A.jar M 

Volver a crear A.jar no es necesario.

Por ejemplo, para obtener una estimación del tamaño de la memoria de un objeto de un tipo arbitrario sin campos, colocamos el código en el método principal:

 class C {}; mem("Empty", new C()); // Empty: 16 

El resultado indicado en el comentario muestra que un objeto sin campos ocupa tantos bytes como un objeto de tipo Objeto.

Además, el resultado del programa:

 {class C {int a; } mem(1, new C());} // 1: 16 {class C {int a,b; } mem(2, new C());} // 2: 24 {class C {int a,b,c; } mem(3, new C());} // 3: 24 {class C {int a,b,c,d;} mem(4, new C());} // 4: 32 

indica que cada campo int toma 4 bytes. Observo que aquí cada línea es un bloque separado, que le permite usar el mismo nombre para diferentes clases.

Cada campo largo es de 8 bytes:

 {class C {long a; } mem(1, new C());} // 1: 24 {class C {long a,b; } mem(2, new C());} // 2: 32 {class C {long a,b,c;} mem(3, new C());} // 3: 40 

Cada campo booleano toma 1 byte (para esta VM):

 {class C {boolean a; } mem(1, new C());} // 1: 16 {class C {boolean a,b; } mem(2, new C());} // 2: 16 {class C {boolean a,b,c; } mem(3, new C());} // 3: 16 {class C {boolean a,b,c,d; } mem(4, new C());} // 4: 16 {class C {boolean a,b,c,d,e;} mem(5, new C());} // 5: 24 

Cada campo de referencia toma 4 bytes (para esta VM):

 {class C {Boolean a; } mem(1, new C());} // 1: 16 {class C {Integer a; } mem(1, new C());} // 1: 16 {class C {Long a; } mem(1, new C());} // 1: 16 {class C {C a; } mem(1, new C());} // 1: 16 {class C {Boolean a,b; } mem(2, new C());} // 2: 24 {class C {Integer a,b; } mem(2, new C());} // 2: 24 {class C {Long a,b; } mem(2, new C());} // 2: 24 {class C {C a,b; } mem(2, new C());} // 2: 24 {class C {Boolean a,b,c; } mem(3, new C());} // 3: 24 {class C {Integer a,b,c; } mem(3, new C());} // 3: 24 {class C {Long a,b,c; } mem(3, new C());} // 3: 24 {class C {C a,b,c; } mem(3, new C());} // 3: 24 {class C {Boolean a,b,c,d;} mem(4, new C());} // 4: 32 {class C {Integer a,b,c,d;} mem(4, new C());} // 4: 32 {class C {Long a,b,c,d;} mem(4, new C());} // 4: 32 {class C {C a,b,c,d;} mem(4, new C());} // 4: 32 

Un campo de tipo cadena también toma 4 bytes, como cada referencia:

 {class C {String a; } mem(" null", new C());} // null: 16 {class C {String a=""; } mem(" empty", new C());} // empty: 16 {class C {String a="A"; } mem("1-char", new C());} // 1-char: 16 {class C {String a="1234567";} mem("7-char", new C());} // 7-char: 16 

Un campo de referencia de matriz también toma 4 bytes, como cada referencia:

 {class C {int[] a; } mem("null", new C());} // null: 16 {class C {int[] a = {}; } mem(" 0", new C());} // 0: 16 {class C {int[] a = new int[1]; } mem(" 1", new C());} // 1: 16 {class C {int[] a = new int[7]; } mem(" 7", new C());} // 7: 16 {class C {int[][] a = {}; } mem(" 00", new C());} // 00: 16 {class C {int[][] a = new int[1][1];} mem(" 11", new C());} // 11: 16 {class C {int[][] a = new int[7][7];} mem(" 77", new C());} // 77: 16 

El objeto de subtipo contiene cada campo declarado en la superclase, independientemente del modificador de acceso:

 {class S { } class C extends S {long a;} mem("0+1", new C());} // 0+1: 24 {class S {private long a;} class C extends S { } mem("1+0", new C());} // 1+0: 24 

El objeto de subtipo contiene un campo declarado en la superclase con el mismo nombre que en la subclase (el llamado oculto - oculto):

 {class S { } class C extends S {long a,b;} mem("0+2", new C());} // 0+2: 32 {class S {long a;} class C extends S {long a; } mem("1+1", new C());} // 1+1: 32 

Un objeto de subtipo contiene cada campo declarado en cada una de sus superclases:

 class U {private long a; } class S extends U {private long a; } class C extends S { long a; } mem("1+1+1", new C()); // 1+1+1: 40 class D { long a,b,c;} mem("0+0+3", new D()); // 0+0+3: 40 

A su vez a las matrices. Como sabes, una matriz es un tipo especial de objeto cuyos elementos están en el objeto mismo, por lo que el tamaño de la memoria ocupada por la matriz crece con el número de elementos:

 {long[] a = new long[ 0]; mem(" 0", a);} // 0: 16 {long[] a = new long[ 1]; mem(" 1", a);} // 1: 24 {long[] a = new long[ 2]; mem(" 2", a);} // 2: 32 {long[] a = new long[ 3]; mem(" 3", a);} // 3: 40 {long[] a = new long[100]; mem("100", a);} // 100: 816 

Y para la variedad de enlaces:

 {Long[] a = new Long[ 0]; mem(" 0", a);} // 0: 16 {Long[] a = new Long[ 1]; mem(" 1", a);} // 1: 24 {Long[] a = new Long[ 2]; mem(" 2", a);} // 2: 24 {Long[] a = new Long[ 3]; mem(" 3", a);} // 3: 32 {Long[] a = new Long[100]; mem("100", a);} // 100: 416 

Ahora, por curiosidad, comparamos los tamaños de varios objetos de diferentes tipos:

 mem(" Object", new Object()); // Object: 16 mem(" String", new String("ABC")); // String: 24 mem(" Exception", new Exception()); // Exception: 40 mem(" int.class", int.class); // int.class: 112 mem(" int[].class", int[].class); // int[].class: 112 mem("Object.class", Object.class); // Object.class: 112 mem("System.class", System.class); // System.class: 160 mem("String.class", String.class); // String.class: 136 

Lo mismo para diferentes jdk en un procesador de 64 bits:

                 jdk1.6.0_45 jdk1.7.0_80 jdk1.8.0_191 jdk-9 jdk-12 jdk-13
                 ----------- ----------- ------------ ------ ------ ---- -
       Objeto: 16 16 16 16 16 16
       Cadena: 32 24 24 24 24 24
    Excepción: 32 32 32 40 40 40
    clase int .: 104 88 104 112 104 112
  int []. clase: 584 544 480 112 104 112
 Clase de objeto: 600560496112104121
 Clase del sistema: 624560496144152160
 Cadena.clase: 696 640 624 136 128 136 

La estimación del tamaño de la cadena es de 24 bytes, aunque la clase String contiene muchas variables estáticas, métodos estáticos y no estáticos. Indudablemente, esto indica la ausencia de variables estáticas y código de método en el objeto. Lo mismo es cierto para un objeto de cualquier tipo.

En conclusión, un recordatorio: todos los datos sobre el tamaño del objeto se estiman y pueden variar hasta cierto punto de una ejecución a otra y, por supuesto, para diferentes máquinas virtuales.

Source: https://habr.com/ru/post/470441/


All Articles