En Java, ¿cuál es la mejor manera de determinar el tamaño de un objeto?

617

Tengo una aplicación que lee un archivo CSV con montones de filas de datos. Le doy al usuario un resumen del número de filas en función de los tipos de datos, pero quiero asegurarme de no leer demasiadas filas de datos y causar OutOfMemoryErrors. Cada fila se traduce en un objeto. ¿Hay una manera fácil de averiguar el tamaño de ese objeto mediante programación? ¿Hay alguna referencia que defina qué tan grandes son los tipos primitivos y las referencias de objeto para a VM?

En este momento, tengo un código que dice leer hasta 32,000 filas , pero también me gustaría tener un código que diga leer tantas filas como sea posible hasta que haya usado 32 MB de memoria. Tal vez esa es una pregunta diferente, pero aún me gustaría saber.

Jay R.
fuente
Agregué mi Agente con mvn configs y expliqué cómo aquí: stackoverflow.com/a/36102269/711855
juanmf

Respuestas:

460

Puede usar el paquete java.lang.instrument

Compile y ponga esta clase en un JAR:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Agregue lo siguiente a su MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Use getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Invocar con:

java -javaagent:ObjectSizeFetcherAgent.jar C
Stefan Karlsson
fuente
2
@Stefan ¡Buena pista! Puede que diga por favor, ¿cuál será el tamaño de byte[0], byte[1], byte[5], int[0], int[1], int[2]utilizando el enfoque que usted describe? Sería bueno si los resultados incluyen sobrecarga para la longitud de la matriz y la alineación de la memoria.
dma_k
8
Intenté esto y obtuve resultados extraños e inútiles. Las cadenas siempre fueron 32, independientemente del tamaño. Pensé que este era quizás el tamaño del puntero, pero para otra clase inmutable que creé, obtuve 24. Funciona bien para primitivas, pero realmente no necesitas un programa que te diga qué tan grande es un char.
Brel
66
@Brel esta solución es solo una "aproximación de la cantidad de almacenamiento consumido por el objeto especificado", como se especifica en la documentación. También supongo que los autores decidieron establecer el tamaño de una Cadena en 32 bytes (¿solo el puntero?) Debido al conjunto de Cadenas de Java, lo que hace difícil decir si una instancia de Cadena es compartida (almacenada en el grupo) o local y exclusivo de una clase.
Andrei I
11
¿Cómo puedo usar ObjectSizeFetcher, si no exporto jar? Tengo prueba de proyecto java en eclipse.
Yura Shinkarev
3
@brel La razón por la que una Cadena tiene solo 32 bytes independientemente de la longitud real es porque la parte de longitud variable de una cadena se almacena en un char [], que es su propio objeto. Para obtener el tamaño real de un objeto, debe agregar el tamaño de sí mismo y el tamaño de cada objeto al que hace referencia.
tombrown52
117

Debería usar jol , una herramienta desarrollada como parte del proyecto OpenJDK.

JOL (Java Object Layout) es la pequeña caja de herramientas para analizar esquemas de diseño de objetos en JVM. Estas herramientas están utilizando inseguro, JVMTI y Serviceability Agent (SA) en gran medida para decodificar el diseño del objeto, la huella y las referencias reales. Esto hace que JOL sea mucho más preciso que otras herramientas que dependen de volcados de almacenamiento dinámico, suposiciones de especificaciones, etc.

Para obtener los tamaños de primitivas, referencias y elementos de matriz, use VMSupport.vmDetails(). En Oracle JDK 1.8.0_40 que se ejecuta en Windows de 64 bits (utilizado para todos los ejemplos siguientes), este método devuelve

Running 64-bit HotSpot VM.
Using compressed oop with 0-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]

Puede obtener el tamaño superficial de una instancia de objeto usando ClassLayout.parseClass(Foo.class).toPrintable()(opcionalmente pasando una instancia a toPrintable). Este es solo el espacio consumido por una sola instancia de esa clase; no incluye ningún otro objeto referenciado por esa clase. Es no incluyen VM sobrecarga para la cabecera del objeto, la alineación de campo y el relleno. Para java.util.regex.Pattern:

java.util.regex.Pattern object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
      0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
      4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
      8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
     12     4         int Pattern.flags                  0
     16     4         int Pattern.capturingGroupCount    1
     20     4         int Pattern.localCount             0
     24     4         int Pattern.cursor                 48
     28     4         int Pattern.patternLength          0
     32     1     boolean Pattern.compiled               true
     33     1     boolean Pattern.hasSupplementary       false
     34     2             (alignment/padding gap)        N/A
     36     4      String Pattern.pattern                (object)
     40     4      String Pattern.normalizedPattern      (object)
     44     4        Node Pattern.root                   (object)
     48     4        Node Pattern.matchRoot              (object)
     52     4       int[] Pattern.buffer                 null
     56     4         Map Pattern.namedGroups            null
     60     4 GroupHead[] Pattern.groupNodes             null
     64     4       int[] Pattern.temp                   null
     68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

Puede obtener una vista de resumen del tamaño profundo de una instancia de objeto utilizando GraphLayout.parseInstance(obj).toFootprint(). Por supuesto, algunos objetos en la huella podrían compartirse (también referenciados desde otros objetos), por lo que es una aproximación excesiva del espacio que podría recuperarse cuando ese objeto se recolecta basura. Para el resultado de Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(tomado de esta respuesta ), jol informa una huella total de 1840 bytes, de los cuales solo 72 son la instancia del Patrón en sí.

java.util.regex.Pattern instance footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1       112       112   [C
         3       272       816   [Z
         1        24        24   java.lang.String
         1        72        72   java.util.regex.Pattern
         9        24       216   java.util.regex.Pattern$1
        13        24       312   java.util.regex.Pattern$5
         1        16        16   java.util.regex.Pattern$Begin
         3        24        72   java.util.regex.Pattern$BitClass
         3        32        96   java.util.regex.Pattern$Curly
         1        24        24   java.util.regex.Pattern$Dollar
         1        16        16   java.util.regex.Pattern$LastNode
         1        16        16   java.util.regex.Pattern$Node
         2        24        48   java.util.regex.Pattern$Single
        40                1840   (total)

Si en su lugar usa GraphLayout.parseInstance(obj).toPrintable(), jol le dirá la dirección, el tamaño, el tipo, el valor y la ruta de las desreferencias de campo para cada objeto referenciado, aunque eso suele ser demasiado detalle para ser útil. Para el ejemplo de patrón continuo, puede obtener lo siguiente. (Las direcciones probablemente cambiarán entre ejecuciones).

java.util.regex.Pattern object externals:
          ADDRESS       SIZE TYPE                             PATH                           VALUE
         d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
         d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
         d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
         d5e5f328      21664 (something else)                 (somewhere else)               (something else)
         d5e647c8         24 java.lang.String                 .pattern                       (object)
         d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
         d5e64850        448 (something else)                 (somewhere else)               (something else)
         d5e64a10         72 java.util.regex.Pattern                                         (object)
         d5e64a58        416 (something else)                 (somewhere else)               (something else)
         d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
         d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
         d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
         d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
         d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
         d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
         d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
         d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
         d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
         d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
         d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
         d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
         d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
         d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
         d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
         d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
         d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
         d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
         d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
         d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
         d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
         d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
         d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
         d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
         d5e65220        120 (something else)                 (somewhere else)               (something else)
         d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

Las entradas "(algo más)" describen otros objetos en el montón que no forman parte de este gráfico de objetos .

La mejor documentación de jol son las muestras de jol en el repositorio de jol. Los ejemplos demuestran operaciones jol comunes y muestran cómo puede usar jol para analizar VM y elementos internos del recolector de basura.

Jeffrey Bosboom
fuente
18
Esta respuesta debería tener más votos a favor. Definitivamente una muy buena opción para consultar. EDITAR: se verificó que esto se agregó este año mientras se hizo la pregunta en el '08. Probablemente la mejor y más fácil opción para hacer lo que OP pidió en este momento.
alquila el
44
El autor de la herramienta escribió una publicación de blog sobre Jol .
Mike
2
Para determinar el tamaño del objeto "obj" use: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
vigor
Tenga en cuenta que vmDetailses ahora VM.current().details().
Miha_x64
Mira, GraphLayout.parseInstance(instance).toFootprint()me pareció más útil comprender los tamaños de los objetos
Mugen
82

Accidentalmente encontré una clase de Java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", ya en jdk, que es fácil de usar y parece bastante útil para determinar el tamaño de un objeto.

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

resultados:

164192
48
16
48
416
Tom
fuente
3
Lo mismo aquí, estaba probando las otras soluciones propuestas anteriormente y encontré ObjectSizeCalculator. Creo que nadie lo mencionó antes, ya que se introdujo recientemente en el JDK 8 como parte del proyecto Nashorn . Sin embargo, no he encontrado ninguna documentación oficial sobre esta clase en la web.
Henrique Gontijo
No parece considerar las longitudes de cadena. ¿Se trata solo del tamaño en la pila?
jontejj
1
Tengo un hashmap, donde com.carrotsearch.RamUsageEstimator devuelve aproximadamente la mitad de ObjectSizeCalculator. ¿Cual es verdadero? - ¿Cuál es más confiable?
badera
99
Tenga en cuenta que ObjectSizeCalculatorsolo es compatible con HotSpot VM
kellanburket
74

Algunos años atrás, Javaworld tenía un artículo sobre la determinación del tamaño de los objetos compuestos y potencialmente anidados de Java , básicamente caminan a través de la creación de una implementación sizeof () en Java. El enfoque básicamente se basa en otro trabajo en el que las personas identificaron experimentalmente el tamaño de los primitivos y los objetos típicos de Java y luego aplican ese conocimiento a un método que recorre recursivamente un gráfico de objetos para contar el tamaño total.

Siempre será algo menos preciso que una implementación nativa de C simplemente debido a lo que sucede detrás de escena de una clase, pero debería ser un buen indicador.

Alternativamente, un proyecto de SourceForge apropiadamente llamado sizeof que ofrece una biblioteca Java5 con una implementación sizeof ().

PD: No utilice el enfoque de serialización, no hay correlación entre el tamaño de un objeto serializado y la cantidad de memoria que consume cuando está en vivo.

Boris Terzic
fuente
66
El tamaño de la utilidad es probablemente la forma más rápida. Básicamente es lo que dijo Stefan, pero ya empacado en un frasco listo para usar.
Alexandre L Telles
62

En primer lugar, "el tamaño de un objeto" no es un concepto bien definido en Java. Podría referirse al objeto en sí, con solo sus miembros, el Objeto y todos los objetos a los que se refiere (el gráfico de referencia). Puede querer decir el tamaño en la memoria o el tamaño en el disco. Y la JVM puede optimizar cosas como Strings.

Entonces, la única forma correcta es preguntarle a la JVM, con un buen generador de perfiles (uso YourKit ), que probablemente no sea lo que desea.

Sin embargo, según la descripción anterior, parece que cada fila será autónoma y no tendrá un gran árbol de dependencias, por lo que el método de serialización probablemente será una buena aproximación en la mayoría de las JVM. La forma más fácil de hacer esto es la siguiente:

 Serializable ser;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(ser);
 oos.close();
 return baos.size();

Recuerde que si tiene objetos con referencias comunes, esto no dará el resultado correcto, y el tamaño de la serialización no siempre coincidirá con el tamaño en la memoria, pero es una buena aproximación. El código será un poco más eficiente si inicializa el tamaño ByteArrayOutputStream a un valor razonable.

Nick Fortescue
fuente
2
Me gusta este enfoque. ¿Qué tan lejos en términos de tamaño de objeto has estado?
Berlin Brown el
1
Muy simple y efectivo. Otros métodos son demasiado desordenados (especialmente dentro de Eclipse RCP). Gracias.
marcolopes
19
La serialización no hará un seguimiento de las variables transitorias, y el método de serialización predeterminado escribe cadenas en UTF-8, por lo que los caracteres ANSI solo tomarán un byte. Si tiene muchas cuerdas, su tamaño estará tan lejos como para ser inútil.
TMN
1
Si bien esto puede no dar el tamaño exacto, para mis necesidades solo necesitaba una comparación entre 2 objetos y SizeOf no se inicializará desde una aplicación web. ¡Gracias!
Isaac
1
Buena recomendación de YourKit . Otras alternativas son VirtualVM y jvmmonitor
angelcervera
38

Si solo desea saber cuánta memoria se está utilizando en su JVM y cuánta es gratis, puede intentar algo como esto:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

editar: pensé que esto podría ser útil ya que el autor de la pregunta también afirmó que le gustaría tener una lógica que maneje "leer tantas filas como sea posible hasta que haya usado 32 MB de memoria".

mate b
fuente
24
Esta no es una buena solución, ya que nunca se sabe cuándo ocurrirá una recolección de basura, o cuánta memoria adicional se asignará al montón de una vez.
Nick Fortescue
55
Eso es cierto, y no pretendo que esto aborde la pregunta principal de esta publicación, pero podría ayudarlo a saber mediante programación cuándo se está acercando un poco al tamaño máximo de almacenamiento dinámico.
mate b
1
Otro problema de esta solución es cuando se encuentra en un entorno de subprocesos múltiples (como en un servidor web). Es posible que otros hilos estuvieran en ejecución y consumieran memoria. Con esta aproximación, está calculando la memoria usada en toda la máquina virtual.
angelcervera
1
Otra desventaja es que freeMemory devuelve una aproximación. Intente crear un objeto javax.crypto.Cipher. ¡La diferencia entre dos llamadas a freeMemory (para estimar el tamaño de un Cipher) no es constante!
Eugen
1
Creo que puede forzar una recolección de basura, por lo que puede hacer algunas cosas en este enfoque.
matanster
24

Cuando trabajaba en Twitter, escribí una utilidad para calcular el tamaño de objeto profundo. Tiene en cuenta diferentes modelos de memoria (32 bits, oops comprimidos, 64 bits), relleno, relleno de subclase, funciona correctamente en estructuras de datos circulares y matrices. Simplemente puede compilar este archivo .java; no tiene dependencias externas:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

Attila Szegedi
fuente
1
Szia! También me gustaría destacar su presentación : las diapositivas 15-20 son excelentes para ayudar a tener una idea instintiva del costo de varias decisiones de estructura de datos. Gracias por publicar eso!
Luke Usherwood el
16
"no tiene dependencias externas", ¿desde cuándo la guayaba no es una dependencia externa?
l4mpi
Guave es una dependencia externa.
Mert Serimer
18

Muchas de las otras respuestas proporcionan tamaños poco profundos, por ejemplo, el tamaño de un HashMap sin ninguna de las claves o valores, que probablemente no sea lo que desea.

El proyecto jamm usa el paquete java.lang.instrumentation anterior, pero recorre el árbol y, por lo tanto, puede darle un uso profundo de la memoria.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Para usar MemoryMeter, inicie la JVM con "-javaagent: /jamm.jar"

Rico
fuente
11

Tienes que caminar los objetos usando la reflexión. Ten cuidado como lo haces:

  • Solo asignar un objeto tiene algo de sobrecarga en la JVM. La cantidad varía según JVM, por lo que puede hacer que este valor sea un parámetro. Al menos conviértalo en una constante (¿8 bytes?) Y aplique a todo lo asignado.
  • El hecho de que bytesea ​​teóricamente 1 byte no significa que solo se necesita uno en la memoria.
  • Habrá bucles en las referencias de objetos, por lo que deberá mantener un HashMapo algo así usando object-equals como el comparador para eliminar bucles infinitos.

@ jodonnell: Me gusta la simplicidad de su solución, pero muchos objetos no son serializables (por lo que esto generaría una excepción), los campos pueden ser transitorios y los objetos pueden anular los métodos estándar.

Jason Cohen
fuente
¿No se definen los tamaños de varias primitivas en la especificación Java? (§2.4.1)
erickson
44
No en el sentido de "cuánta memoria ocupa", que es la pregunta. Solo en el sentido de cómo operan. Por ejemplo, bytes, caracteres, y pantalones cortos ocupan una palabra completa en la pila de Java, a pesar de que operan con redondeo, etc ..
Jason Cohen
1
Esto suena similar a medir el tamaño, como lo muestra Heinz en su Boletín # 78: javaspecialists.eu/archive/Issue078.html . Lo usé. Su enfoque funciona.
Peter Kofler
8

Debe medirlo con una herramienta, o estimarlo a mano, y depende de la JVM que esté utilizando.

Hay una sobrecarga fija por objeto. Es específico de JVM, pero generalmente calculo 40 bytes. Luego tienes que mirar a los miembros de la clase. Las referencias a objetos son 4 (8) bytes en una JVM de 32 bits (64 bits). Los tipos primitivos son:

  • booleano y byte: 1 byte
  • char y short: 2 bytes
  • int y float: 4 bytes
  • largo y doble: 8 bytes

Las matrices siguen las mismas reglas; es decir, es una referencia de objeto, por lo que toma 4 (u 8) bytes en su objeto, y luego su longitud multiplicada por el tamaño de su elemento.

Intentar hacerlo programáticamente con llamadas a Runtime.freeMemory()simplemente no le da mucha precisión, debido a las llamadas asincrónicas al recolector de basura, etc. Perfilar el montón con -Xrunhprof u otras herramientas le dará los resultados más precisos.

erickson
fuente
@erickson No estaría seguro acerca de sizeof (boolean) == 1 mirando este hilo ( stackoverflow.com/questions/1907318/… ). ¿Puedes comentar sobre esto?
dma_k
2
@dma_k, Java no tiene booleanos reales en realidad. El tamaño del booleano es de 4bytes fuera de las matrices y 1byte dentro boolean[]. En realidad, todos los tipos primitivos no dobles / largos son de 4 bytes. Los últimos son 8 (la respuesta los coloca erróneamente como 4 también)
mejor
@bestsss: para ser más exactos, la asignación mínima de memoria depende de la plataforma y la implementación de JVM. También los objetos en el montón están alineados, por lo que después de sumar todos los tamaños, uno necesita redondear.
dma_k
6

La java.lang.instrument.Instrumentationclase proporciona una buena manera de obtener el tamaño de un Objeto Java, pero requiere que defina ay premainejecute su programa con un agente Java. Esto es muy aburrido cuando no necesita ningún agente y luego tiene que proporcionar un agente Jar ficticio para su aplicación.

Entonces obtuve una solución alternativa usando la Unsafeclase de sun.misc. Entonces, considerando la alineación del montón de objetos de acuerdo con la arquitectura del procesador y calculando el desplazamiento de campo máximo, puede medir el tamaño de un Objeto Java. En el siguiente ejemplo, uso una clase auxiliar UtilUnsafepara obtener una referencia al sun.misc.Unsafeobjeto.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}
Miguel Gamboa
fuente
Enfoque interesante, pero ¿no supone esto que el objeto y su almacenamiento de campos no están fragmentados?
nicoulaj
Sí, y no conozco ninguna implementación de JVM que haga tal fragmentación.
Miguel Gamboa
No entiendo. La fragmentación no es una opción :) Tomemos el ejemplo del objeto C que se almacena como un campo de objetos A y B. ¿No cambia todo en A o B?
nicoulaj
Lo siento, pero tampoco entiendo tu punto de vista. Según mi interpretación, en Java los objetos no se pueden almacenar dentro de otros objetos, como sucede con las estructuras C o los tipos de valor en .Net. Entonces, cuando dices: "objeto C, que se almacena como un campo de objetos A y B", eso significa que los objetos A y B tienen campos que almacenan referencias (punteros) al objeto C. Luego, el tamaño de A y B es igual a el desplazamiento de ese campo más el tamaño de una referencia (puntero) al objeto C. Y el tamaño de una referencia es el tamaño de una palabra.
Miguel Gamboa
Oh, está bien, estamos hablando de un tamaño poco profundo. Culpa mía.
nicoulaj
6

También existe la herramienta Memory Measurer (anteriormente en Google Code , ahora en GitHub ), que es simple y publicada bajo la licencia comercial Apache 2.0 , como se discutió en una pregunta similar .

También, requiere un argumento de línea de comandos para el intérprete de Java si desea medir el consumo de bytes de memoria, pero de lo contrario parece funcionar bien, al menos en los escenarios que lo he usado.

PNS
fuente
4

Sin tener que meterse con la instrumentación, etc., y si no necesita saber el tamaño exacto en bytes de un objeto, puede seguir el siguiente enfoque:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

De esta manera, lee la memoria usada antes y después, y llamando al GC justo antes de obtener la memoria usada, reduce el "ruido" casi a 0.

Para obtener un resultado más confiable, puede ejecutar su trabajo n veces, y luego dividir la memoria utilizada por n, obteniendo la cantidad de memoria que requiere una ejecución. Aún más, puedes ejecutarlo todo más veces y hacer un promedio.

muy agradable
fuente
55
¿No System.gc()solo te notifica que quieres GC? No se garantiza que se llame al GC en absoluto.
Raildex
@muy agradable. Esto no es seguro porque nunca puede lo que GC hace o afecta la memoria entre sus líneas. Así que "entre" dos métodos FreeMemory GC puede liberar más espacio que usted no se considera por lo tanto que el objeto se verá más pequeña
Mert Serimer
@MertSerimer "no es seguro" está en un nivel completamente diferente para mí: a lo sumo, esto no es tan preciso, como también dije. Además, no puede conducir el GC (como dijo Raildex), pero para este caso también sugerí insertar esto en un ciclo. Este es solo un sistema rápido, sucio y aproximado que funciona si el resultado no necesita ser muy confiable, como se indicó.
realmente agradable
Hay muchos problemas con esto, pero te da un buen botín.
markthegrea
3

Aquí hay una utilidad que hice usando algunos de los ejemplos vinculados para manejar 32 bits, 64 bits y 64 bits con OOP comprimido. Lo utiliza sun.misc.Unsafe.

Se utiliza Unsafe.addressSize()para obtener el tamaño de un puntero nativo y Unsafe.arrayIndexScale( Object[].class )el tamaño de una referencia de Java.

Utiliza el desplazamiento de campo de una clase conocida para calcular el tamaño base de un objeto.

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;

/** Usage: 
 * MemoryUtil.sizeOf( object )
 * MemoryUtil.deepSizeOf( object )
 * MemoryUtil.ADDRESS_MODE
 */
public class MemoryUtil
{
    private MemoryUtil()
    {
    }

    public static enum AddressMode
    {
        /** Unknown address mode. Size calculations may be unreliable. */
        UNKNOWN,
        /** 32-bit address mode using 32-bit references. */
        MEM_32BIT,
        /** 64-bit address mode using 64-bit references. */
        MEM_64BIT,
        /** 64-bit address mode using 32-bit compressed references. */
        MEM_64BIT_COMPRESSED_OOPS
    }

    /** The detected runtime address mode. */
    public static final AddressMode ADDRESS_MODE;

    private static final Unsafe UNSAFE;

    private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
    private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
    private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
    private static final long OBJECT_ALIGNMENT = 8;

    /** Use the offset of a known field to determine the minimum size of an object. */
    private static final Object HELPER_OBJECT = new Object() { byte b; };


    static
    {
        try
        {
            // Use reflection to get a reference to the 'Unsafe' object.
            Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
            f.setAccessible( true );
            UNSAFE = (Unsafe) f.get( null );

            OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );

            ADDRESS_SIZE = UNSAFE.addressSize();
            REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );

            if( ADDRESS_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_32BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT;
            }
            else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
            {
                ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
            }
            else
            {
                ADDRESS_MODE = AddressMode.UNKNOWN;
            }
        }
        catch( Exception e )
        {
            throw new Error( e );
        }
    }


    /** Return the size of the object excluding any referenced objects. */
    public static long shallowSizeOf( final Object object )
    {
        Class<?> objectClass = object.getClass();
        if( objectClass.isArray() )
        {
            // Array size is base offset + length * element size
            long size = UNSAFE.arrayBaseOffset( objectClass )
                    + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
            return padSize( size );
        }
        else
        {
            // Object size is the largest field offset padded out to 8 bytes
            long size = OBJECT_BASE_SIZE;
            do
            {
                for( Field field : objectClass.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 )
                    {
                        long offset = UNSAFE.objectFieldOffset( field );
                        if( offset >= size )
                        {
                            size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
                        }
                    }
                }
                objectClass = objectClass.getSuperclass();
            }
            while( objectClass != null );

            return padSize( size );
        }
    }


    private static final long padSize( final long size )
    {
        return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
    }


    /** Return the size of the object including any referenced objects. */
    public static long deepSizeOf( final Object object )
    {
        IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
        Stack<Object> stack = new Stack<Object>();
        if( object != null ) stack.push( object );

        long size = 0;
        while( !stack.isEmpty() )
        {
            size += internalSizeOf( stack.pop(), stack, visited );
        }
        return size;
    }


    private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
    {
        // Scan for object references and add to stack
        Class<?> c = object.getClass();
        if( c.isArray() && !c.getComponentType().isPrimitive() )
        {
            // Add unseen array elements to stack
            for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
            {
                Object val = Array.get( object, i );
                if( val != null && visited.put( val, val ) == null )
                {
                    stack.add( val );
                }
            }
        }
        else
        {
            // Add unseen object references to the stack
            for( ; c != null; c = c.getSuperclass() )
            {
                for( Field field : c.getDeclaredFields() )
                {
                    if( (field.getModifiers() & Modifier.STATIC) == 0 
                            && !field.getType().isPrimitive() )
                    {
                        field.setAccessible( true );
                        try
                        {
                            Object val = field.get( object );
                            if( val != null && visited.put( val, val ) == null )
                            {
                                stack.add( val );
                            }
                        }
                        catch( IllegalArgumentException e )
                        {
                            throw new RuntimeException( e );
                        }
                        catch( IllegalAccessException e )
                        {
                            throw new RuntimeException( e );
                        }
                    }
                }
            }
        }

        return shallowSizeOf( object );
    }
}
dlaudams
fuente
¿Probaste esta clase con valores? Lo intenté, pero para mí, valores incorrectos !!!.
Débora
1
Los valores que me dio para un objeto simple eran casi correctos, pero desactivados por un factor de 10 para una lista que contenía 1 millón de objetos. Aún así, muy buen trabajo!
Michael Böckling
Interesante. Lo probé usando JDK7u67, en Windows 7 x64 y Linux 2.6.16 / x86_64, usando cada uno de los modos de dirección 32bit / 64bit / oop. Lo he comparado con los volcados de memoria analizados en Eclipse Memory Analyzer 1.3.x. ¿Qué configuración estás usando? ¿Tienes un ejemplo específico que podría probar?
dlaudams
La mejor opción que puedo hacer. No puedo usar Instrumentationporque no inicio tomcat, ObjectSizeCalculatorporque no estoy seguro del tipo de VM (HotSpot) y JOLlos granos de primavera bacouse. Utilizo esto y agrego un segundo parámetro para ignorar las señales simples AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()y el internalSizeOfcódigo de refactorización para ignorar Class y Enum
Perlos
Para comparar resultados, use ObjectSizeCalculator (Compute todo el servidor de 1GB a 10s). JOL causa MemError (no se incluyen 6GB) y no obtengo los mismos resultados, probablemente debido a las enumeraciones.
Perlos
3

Estaba buscando un cálculo de tiempo de ejecución de un tamaño de objeto que cumpliera con los siguientes requisitos:

  • Disponible en tiempo de ejecución sin necesidad de incluir instrumentación.
  • Funciona con Java 9+ sin acceso a inseguro.
  • Se basa solo en la Clase. No es un tamaño profundo que tenga en cuenta longitudes de cadena, longitudes de matriz, etc.

Lo siguiente se basa en el código central del artículo original de especialistas de Java ( https://www.javaspecialists.eu/archive/Issue078.html ) y algunos bits de la versión insegura en otra respuesta a esta pregunta.

Espero que alguien lo encuentre útil.

public class JavaSize {

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS / BYTE;
private static final int HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}

David Ryan
fuente
2

No hay una llamada al método, si eso es lo que está pidiendo. Con un poco de investigación, supongo que podrías escribir la tuya. Una instancia particular tiene un tamaño fijo derivado del número de referencias y valores primitivos más datos de contabilidad de la instancia. Simplemente caminaría el gráfico de objetos. Cuanto menos variados son los tipos de filas, más fácil.

Si eso es demasiado lento o simplemente más problemas de lo que vale, siempre hay una buena regla de conteo a la antigua.

sblundy
fuente
2

Escribí una prueba rápida una vez para estimar sobre la marcha:

public class Test1 {

    // non-static nested
    class Nested { }

    // static nested
    static class StaticNested { }

    static long getFreeMemory () {
        // waits for free memory measurement to stabilize
        long init = Runtime.getRuntime().freeMemory(), init2;
        int count = 0;
        do {
            System.out.println("waiting..." + init);
            System.gc();
            try { Thread.sleep(250); } catch (Exception x) { }
            init2 = init;
            init = Runtime.getRuntime().freeMemory();
            if (init == init2) ++ count; else count = 0;
        } while (count < 5);
        System.out.println("ok..." + init);
        return init;
    }

    Test1 () throws InterruptedException {

        Object[] s = new Object[10000];
        Object[] n = new Object[10000];
        Object[] t = new Object[10000];

        long init = getFreeMemory();

        //for (int j = 0; j < 10000; ++ j)
        //    s[j] = new Separate();

        long afters = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            n[j] = new Nested();

        long aftersn = getFreeMemory();

        for (int j = 0; j < 10000; ++ j)
            t[j] = new StaticNested();

        long aftersnt = getFreeMemory();

        System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
        System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
        System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);

    }

    public static void main (String[] args) throws InterruptedException {
        new Test1();
    }

}

El concepto general es asignar objetos y medir el cambio en el espacio de almacenamiento dinámico libre. La clave es getFreeMemory(), que solicita ejecuciones de GC y espera a que se estabilice el tamaño de almacenamiento dinámico libre informado . La salida de lo anterior es:

nested:        160000 each=16
static nested: 160000 each=16

Que es lo que esperamos, dado el comportamiento de alineación y la posible sobrecarga del encabezado del bloque de montón.

El método de instrumentación detallado en la respuesta aceptada aquí es el más preciso. El método que describí es preciso pero solo bajo condiciones controladas donde ningún otro hilo está creando / descartando objetos.

Jason C
fuente
2

Solo usa java visual VM.

Tiene todo lo que necesita para perfilar y depurar problemas de memoria.

También tiene una consola OQL (Object Query Language) que le permite hacer muchas cosas útiles, una de las cuales es sizeof(o)

ACV
fuente
2

Cuando use JetBrains IntelliJ, primero habilite "Adjuntar agente de memoria" en Archivo | Configuraciones | Construcción, Ejecución, Implementación | Depurador

Al depurar, haga clic con el botón derecho en una variable de interés y elija "Calcular tamaño retenido": Calcular tamaño retenido

simon04
fuente
1

Mi respuesta se basa en el código proporcionado por Nick. Ese código mide la cantidad total de bytes que están ocupados por el objeto serializado. Entonces, esto realmente mide cosas de serialización + huella de memoria de objeto simple (solo serializa, por ejemplo, inty verás que la cantidad total de bytes serializados no lo es 4). Entonces, si desea obtener el número de bytes sin procesar utilizado exactamente para su objeto, debe modificar ese código un poco. Al igual que:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectSizeCalculator {
    private Object getFirstObjectReference(Object o) {
        String objectType = o.getClass().getTypeName();

        if (objectType.substring(objectType.length()-2).equals("[]")) {
            try {
                if (objectType.equals("java.lang.Object[]"))
                    return ((Object[])o)[0];
                else if (objectType.equals("int[]"))
                    return ((int[])o)[0];
                else
                    throw new RuntimeException("Not Implemented !");
            } catch (IndexOutOfBoundsException e) {
                return null;
            }
        }

        return o;
    } 

    public int getObjectSizeInBytes(Object o) {
        final String STRING_JAVA_TYPE_NAME = "java.lang.String";

        if (o == null)
            return 0;

        String objectType = o.getClass().getTypeName();
        boolean isArray = objectType.substring(objectType.length()-2).equals("[]");

        Object objRef = getFirstObjectReference(o);
        if (objRef != null && !(objRef instanceof Serializable))
            throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            oos.close();
            byte[] bytes = baos.toByteArray();

            for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
                if (objectType != STRING_JAVA_TYPE_NAME) {
                    if (bytes[i] == 112)
                        if (isArray)
                            return j - 4;
                        else
                            return j;
                } else {
                    if (bytes[i] == 0)
                        return j - 1;
                }
            }
        } catch (Exception e) {
            return -1;
        }

        return -1;
    }    

}

He probado esta solución con tipos primitivos, String y en algunas clases triviales. Puede que no haya casos cubiertos también.


ACTUALIZACIÓN: Ejemplo modificado para admitir el cálculo de la huella de memoria de los objetos de la matriz.

Agnius Vasiliauskas
fuente
0

Podría generar un volcado de almacenamiento dinámico (con jmap, por ejemplo) y luego analizar la salida para encontrar tamaños de objeto. Esta es una solución fuera de línea, pero puede examinar tamaños poco profundos y profundos, etc.

JZeeb
fuente
0
long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

size le proporciona el aumento en el uso de memoria de jvm debido a la creación de objetos y que normalmente es el tamaño del objeto.

user835199
fuente
¿Qué pasa si GC se ejecuta en el medio durante // Código para la construcción de objetos? Ahora podría producir resultados correctos todo el tiempo.
rajugaadu
0

Esta respuesta no está relacionada con el tamaño del objeto, pero cuando está utilizando una matriz para acomodar los objetos; cuánto tamaño de memoria asignará al objeto.

Por lo tanto, las matrices, la lista o el mapa de todas esas colecciones no van a almacenar objetos realmente (solo en el momento de las primitivas, se necesita un tamaño de memoria de objeto real), solo almacenará referencias para esos objetos.

Ahora el Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 bytes) depende del SO (32/64 bit)

Primitivas

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

OBJETOS

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

Quiero decir que todo el objeto REFERENCE necesita solo 4 bytes de memoria. Puede ser una referencia de cadena o una referencia de objeto doble, pero depende de la creación del objeto, la memoria necesaria variará.

ej.) Si creo un objeto para la clase siguiente, se crearán ReferenceMemoryTest4 + 4 + 4 = 12 bytes de memoria. La memoria puede diferir cuando intenta inicializar las referencias.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Entonces, cuando se crea una matriz de objeto / referencia, todo su contenido estará ocupado con referencias NULL. Y sabemos que cada referencia requiere 4 bytes.

Y finalmente, la asignación de memoria para el siguiente código es de 20 bytes.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bytes) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bytes)

Kanagavelu Sugumar
fuente
1
¿Cómo pueden un entero de 4 bytes y una referencia de objeto de tamaño desconocido encajar en 4 bytes?
Marqués de Lorne
@EJP Quiero decir que todo el objeto REFERENCE necesita solo 4 bytes de memoria. Puede ser una referencia de cadena O una referencia de objeto doble, pero depende de la creación del objeto, la memoria necesaria variará.
Kanagavelu Sugumar
0

Supongamos que declaro una clase llamada Complexcomo:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Para ver cuánta memoria se asigna a las instancias activas de esta clase:

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex
Ali Dehghani
fuente
-5

Para JSONObject, el siguiente código puede ayudarlo.

`JSONObject.toString().getBytes("UTF-8").length`

devuelve tamaño en bytes

Lo comprobé con mi objeto JSONArray escribiéndolo en un archivo. Está dando el tamaño del objeto.

solokiran
fuente
esto solo funcionaría para objetos que son principalmente cadenas.
Dexter Legaspi
-6

Dudo que quieras hacerlo mediante programación a menos que solo quieras hacerlo una vez y almacenarlo para usarlo en el futuro. Es algo costoso de hacer. No hay un operador sizeof () en Java, e incluso si lo hubiera, solo contaría el costo de las referencias a otros objetos y el tamaño de las primitivas.

Una forma de hacerlo es serializar la cosa en un archivo y ver el tamaño del archivo, así:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Por supuesto, esto supone que cada objeto es distinto y no contiene referencias no transitorias a otra cosa.

Otra estrategia sería tomar cada objeto y examinar sus miembros por reflexión y sumar los tamaños (boolean & byte = 1 byte, short & char = 2 bytes, etc.), avanzando en la jerarquía de miembros. Pero eso es tedioso y costoso y termina haciendo lo mismo que haría la estrategia de serialización.

jodonnell
fuente
3
Lo serializaría en un byte [] usando un ByteArrayOutputStream. Sería mucho más rápido que escribirlo en un archivo.
ScArcher2
@KorayTugay Determinar el tamaño de byte de un objeto ya es una operación costosa. Escribir cada objeto en el disco para determinar el tamaño, solo hará que se arrastre ...
HammerNL
1
El formato del objeto serializado es completamente diferente al formato del objeto en la memoria del montón. En particular, se escribe un descriptor para la clase del objeto (y todas sus superclases serializables) en la secuencia. Entonces, escribir una instancia simple de java.lang.Integerproduce alrededor de 80 bytes, donde la representación del montón generalmente es 32 (a diferencia de la representación del flujo de objetos, la representación del montón depende del tamaño del puntero y la alineación del objeto). En contraste, una nullreferencia serializada requiere un byte en lugar de los cuatro u ocho bytes en la memoria de almacenamiento dinámico.
Holger