¿Cuál es el consumo de memoria de un objeto en Java?

10 minutos de lectura

¿Es el espacio de memoria consumido por un objeto con 100 atributos el mismo que el de 100 objetos, con un atributo cada uno?

¿Cuánta memoria se asigna para un objeto?
¿Cuánto espacio adicional se usa al agregar un atributo?

¿Cual es el consumo de memoria de un objeto en
VonC

Mindprod señala que esta no es una pregunta fácil de responder:

Una JVM es libre de almacenar datos de la forma que le plazca internamente, big o little endian, con cualquier cantidad de relleno o sobrecarga, aunque las primitivas deben comportarse como si tuvieran los tamaños oficiales.
Por ejemplo, la JVM o el compilador nativo pueden decidir almacenar un boolean[] en fragmentos largos de 64 bits como un BitSet. No tiene que decírselo, siempre que el programa dé las mismas respuestas.

  • Podría asignar algunos objetos temporales en la pila.
  • Puede optimizar algunas variables o llamadas a métodos totalmente fuera de existencia reemplazándolas con constantes.
  • Puede versionar métodos o bucles, es decir, compilar dos versiones de un método, cada una optimizada para una determinada situación, y luego decidir por adelantado a cuál llamar.

Luego, por supuesto, el hardware y el sistema operativo tienen cachés multicapa, en caché de chip, caché SRAM, caché DRAM, conjunto de trabajo RAM ordinario y almacenamiento de respaldo en disco. Sus datos pueden estar duplicados en cada nivel de caché. Toda esta complejidad significa que solo puede predecir de manera muy aproximada el consumo de RAM.

Métodos de medición

Puedes usar Instrumentation.getObjectSize() para obtener una estimación del almacenamiento consumido por un objeto.

Para visualizar el real disposición del objeto, huella y referencias, puede utilizar el Herramienta JOL (diseño de objetos Java).

Encabezados de objetos y referencias de objetos

En un JDK moderno de 64 bits, un objeto tiene un encabezado de 12 bytes, rellenado a un múltiplo de 8 bytes, por lo que el tamaño mínimo del objeto es de 16 bytes. Para las JVM de 32 bits, la sobrecarga es de 8 bytes, ampliada a un múltiplo de 4 bytes. (De la respuesta de Dmitry Spikhalskiy, la respuesta de Jayen y JavaMundo.)

Por lo general, las referencias son de 4 bytes en plataformas de 32 bits o en plataformas de 64 bits hasta -Xmx32G; y 8 bytes por encima de 32 Gb (-Xmx32G). (Ver referencias a objetos comprimidos.)

Como resultado, una JVM de 64 bits normalmente requeriría entre un 30 y un 50 % más de espacio de almacenamiento dinámico. (¿Debo usar una JVM de 32 o de 64 bits?2012, JDK 1.7)

Tipos en caja, arreglos y cadenas

Los envoltorios en caja tienen una sobrecarga en comparación con los tipos primitivos (desde JavaMundo):

  • Integer: El resultado de 16 bytes es un poco peor de lo que esperaba porque un int El valor puede caber en solo 4 bytes adicionales. Usando un Integer me cuesta una sobrecarga de memoria del 300 por ciento en comparación con cuando puedo almacenar el valor como un tipo primitivo

  • Long: 16 bytes también: claramente, el tamaño real del objeto en el montón está sujeto a la alineación de memoria de bajo nivel realizada por una implementación de JVM particular para un tipo de CPU en particular. Se ve como un Long es 8 bytes de sobrecarga de objeto, más 8 bytes más para el valor largo real. A diferencia de, Integer tenía un agujero de 4 bytes sin usar, muy probablemente porque la JVM que uso fuerza la alineación de objetos en un límite de palabra de 8 bytes.

Otros contenedores también son costosos:

  • Matrices multidimensionales: ofrece otra sorpresa.
    Los desarrolladores comúnmente emplean construcciones como int[dim1][dim2] en computación numérica y científica.

    en un int[dim1][dim2] instancia de matriz, cada anidado int[dim2] matriz es un Object en su propio derecho. Cada uno agrega la sobrecarga habitual de matriz de 16 bytes. Cuando no necesito una matriz triangular o irregular, eso representa una sobrecarga pura. El impacto crece cuando las dimensiones de la matriz difieren mucho.

    por ejemplo, un int[128][2] instancia toma 3.600 bytes. En comparación con los 1.040 bytes de un int[256] utiliza la instancia (que tiene la misma capacidad), 3600 bytes representan una sobrecarga del 246 por ciento. En el caso extremo de byte[256][1], el factor de gastos generales es casi 19! Compare eso con la situación de C/C++ en la que la misma sintaxis no agrega ninguna sobrecarga de almacenamiento.

  • String: a StringEl crecimiento de la memoria sigue el crecimiento de su matriz interna de caracteres. sin embargo, el String class agrega otros 24 bytes de sobrecarga.

    Para un no vacío String de tamaño de 10 caracteres o menos, el costo adicional adicional en relación con la carga útil útil (2 bytes para cada carácter más 4 bytes para la longitud), oscila entre 100 y 400 por ciento.

Alineación

Considera esto objeto de ejemplo:

class X {                      // 8 bytes for reference to the class definition
   int a;                      // 4 bytes
   byte b;                     // 1 byte
   Integer c = new Integer();  // 4 bytes for a reference
}

Una suma ingenua sugeriría que una instancia de X usaría 17 bytes. Sin embargo, debido a la alineación (también denominada relleno), la JVM asigna la memoria en múltiplos de 8 bytes, por lo que en lugar de 17 bytes, asignaría 24 bytes.

  • En t[128][6]: 128 matrices de 6 entradas – 768 entradas en total, 3072 bytes de datos + 2064 bytes Sobrecarga de objetos = 5166 bytes en total. En t[256]: 256 entradas en total – por lo tanto no comparable. En t[768]: 3072 bytes de datos + 16 bytes de sobrecarga – alrededor de 3/5 del espacio de la matriz 2D – ¡no exactamente un 246% de sobrecarga!

    – JeeBee

    7 de abril de 2009 a las 16:07

  • Ah, el artículo original utilizado int[128][2] no ent[128][6] – Me pregunto cómo cambió eso. También muestra que los ejemplos extremos pueden contar una historia diferente.

    – JeeBee

    7 de abril de 2009 a las 16:09

  • La sobrecarga es de 16 bytes en JVM de 64 bits.

    –Tim Cooper

    12/04/2013 a las 13:00

  • @AlexWien: algunos esquemas de recolección de basura pueden imponer un tamaño de objeto mínimo que está separado del relleno. Durante la recolección de elementos no utilizados, una vez que un objeto se copia de una ubicación anterior a una nueva, es posible que la ubicación anterior ya no necesite contener los datos del objeto, pero deberá contener una referencia a la nueva ubicación; también puede necesitar almacenar una referencia a la ubicación anterior del objeto donde se descubrió la primera referencia y el desplazamiento de esa referencia dentro del objeto anterior [since the old object may still contain references that haven’t yet been processed].

    – Super gato

    19/09/2014 a las 16:34

  • @AlexWien: el uso de la memoria en la ubicación anterior de un objeto para contener la información de contabilidad del recolector de basura evita la necesidad de asignar otra memoria para ese propósito, pero puede imponer un tamaño de objeto mínimo que es mayor de lo que sería necesario. Creo que al menos una versión del recolector de basura .NET usa ese enfoque; ciertamente sería posible que algunos recolectores de basura de Java también lo hicieran.

    – Super gato

    19/09/2014 a las 16:38

¿Cual es el consumo de memoria de un objeto en
Dmitri Spikhalskiy

Depende de la arquitectura/jdk. Para un JDK moderno y una arquitectura de 64 bits, un objeto tiene un encabezado de 12 bytes y un relleno de 8 bytes, por lo que el tamaño mínimo del objeto es de 16 bytes. Puedes usar una herramienta llamada Diseño de objetos Java para determinar un tamaño y obtener detalles sobre el diseño del objeto y la estructura interna de cualquier entidad o adivinar esta información por referencia de clase. Ejemplo de una salida para Integer en mi entorno:

Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

Entonces, para Integer, el tamaño de la instancia es de 16 bytes, porque el int de 4 bytes se compacta en su lugar justo después del encabezado y antes del límite de relleno.

Ejemplo de código:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

Si usa maven, para obtener JOL:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>

1646751197 833 ¿Cual es el consumo de memoria de un objeto en
jon skeet

Cada objeto tiene una cierta sobrecarga para su monitor asociado y tipo de información, así como los propios campos. Más allá de eso, los campos se pueden diseñar prácticamente sin embargo, la JVM lo considera adecuado (creo), pero como se muestra en otra respuesta, al menos algunos Las JVM se empaquetarán bastante apretadas. Considere una clase como esta:

public class SingleByte
{
    private byte b;
}

contra

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

En una JVM de 32 bits, esperaría 100 instancias de SingleByte para tomar 1200 bytes (8 bytes de sobrecarga + 4 bytes para el campo debido al relleno/alineación). Esperaría una instancia de OneHundredBytes para tomar 108 bytes – la sobrecarga, y luego 100 bytes, empaquetados. Sin embargo, ciertamente puede variar según la JVM: una implementación puede decidir no empaquetar los campos en OneHundredBytes, lo que lleva a que tome 408 bytes (= 8 bytes de sobrecarga + 4 * 100 bytes alineados/rellenados). En una JVM de 64 bits, la sobrecarga también puede ser mayor (no estoy seguro).

EDITAR: Vea el comentario a continuación; aparentemente HotSpot rellena límites de 8 bytes en lugar de 32, por lo que cada instancia de SingleByte tomaría 16 bytes.

De cualquier manera, el “único objeto grande” será al menos tan eficiente como varios objetos pequeños, para casos simples como este.

  • En realidad, una instancia de SingleByte tomaría 16 bytes en una JVM de Sun, es decir, 8 bytes de sobrecarga, 4 bytes para el campo y luego 4 bytes para el relleno de objetos, ya que el compilador HotSpot redondea todo a múltiplos de 8.

    –Paul Wagland

    24 de enero de 2011 a las 1:26

Parece que cada objeto tiene una sobrecarga de 16 bytes en sistemas de 32 bits (y 24 bytes en sistemas de 64 bits).

http://algs4.cs.princeton.edu/14analysis/ es una buena fuente de información. Un ejemplo entre muchos buenos es el siguiente.

ingrese la descripción de la imagen aquí

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-ficient-java-tutorial.pdf también es muy informativo, por ejemplo:

ingrese la descripción de la imagen aquí

1646751202 146 ¿Cual es el consumo de memoria de un objeto en
atrapar23

La memoria total utilizada / libre de un programa se puede obtener en el programa a través de

java.lang.Runtime.getRuntime();

El tiempo de ejecución tiene varios métodos que se relacionan con la memoria. El siguiente ejemplo de codificación demuestra su uso.

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create an object Person yourself...
         List <Person> list = new ArrayList <Person> ();
         for (int i = 0; i <= 100_000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }

         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();

         // Run the garbage collector
         runtime.gc();

         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }

¿Es el espacio de memoria consumido por un objeto con 100 atributos el mismo que el de 100 objetos, con un atributo cada uno?

No.

¿Cuánta memoria se asigna para un objeto?

  • La sobrecarga es de 8 bytes en 32 bits, 12 bytes en 64 bits; y luego redondeado a un múltiplo de 4 bytes (32 bits) u 8 bytes (64 bits).

¿Cuánto espacio adicional se usa al agregar un atributo?

  • Los atributos varían de 1 byte (byte) a 8 bytes (largo/doble), pero las referencias son de 4 u 8 bytes dependiendo no sobre si es de 32 bits o de 64 bits, sino más bien si -Xmx es < 32 Gb o > = 32 Gb: las JVM típicas de 64 bits tienen una optimización llamada “-UseCompressedOops” que comprime las referencias a 4 bytes si el montón está por debajo de 32 Gb.

1646751203 744 ¿Cual es el consumo de memoria de un objeto en
Mendelt

No, registrar un objeto también requiere un poco de memoria. 100 objetos con 1 atributo ocuparán más memoria.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad