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


Por ejemplo, digamos que tengo una aplicación que puede leer en un archivo CSV con montones de filas de datos. Le doy al usuario un resumen del número de filas según los tipos de datos, pero quiero asegurarme de no leer en demasiadas filas de datos y causar OutOfMemoryErrors. Cada fila se traduce en un objeto. ¿Hay alguna manera fácil de conocer el tamaño de ese objeto programáticamente? ¿Hay una referencia que define cómo los grandes tipos primitivos y las referencias a objetos son para un 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 la memoria Tal vez esa es una pregunta diferente, pero aún me gustaría saber.


533
2017-09-09 17:07


origen


Respuestas:


Puedes usar el paquete java.lang.instrument

Compila y coloca 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

403
2017-09-09 19:24



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

Siempre va a ser algo menos preciso que una implementación de C nativa simplemente por las cosas que ocurren detrás de escena de una clase, pero debería ser un buen indicador.

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

PD No use el enfoque de serialización, no existe una correlación entre el tamaño de un objeto serializado y la cantidad de memoria que consume cuando está activo.


69
2017-09-09 18:42



Deberías 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 disposición de objetos en JVM. Estas herramientas están utilizando Inseguro, JVMTI y Agente de servicio (SA) en gran medida para decodificar el diseño del objeto real, la huella y las referencias. Esto hace que JOL sea mucho más preciso que otras herramientas que dependen de volcados de almacenamiento dinámico, supuestos de especificación, etc.

Para obtener los tamaños de primitivos, 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 regresa

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. Eso hace incluya sobrecarga de VM para el encabezado del objeto, alineación de campo y relleno. por 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 resumida del tamaño profundo de una instancia de objeto usando GraphLayout.parseInstance(obj).toFootprint(). Por supuesto, algunos objetos en la huella pueden ser compartidos (también referenciados desde otros objetos), por lo que es una aproximación excesiva del espacio que podría reclamarse cuando ese objeto es basura. Por 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 cambio usas 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 al que se hace referencia, aunque normalmente son demasiados detalles para ser útiles. Para el ejemplo de patrón en curso, puede obtener lo siguiente. (Es probable que las direcciones cambien 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)" describir otros objetos en el montón que no son parte de este gráfico de objetos.

La mejor documentación de jol es la jol muestras en el repositorio de jol Las muestras muestran operaciones de jol comunes y muestran cómo se puede usar jol para analizar las partes internas de la VM y del recolector de basura.


60
2018-05-04 00:46



En primer lugar, "el tamaño de un objeto" no es un concepto bien definido en Java. Puede referirse al objeto en sí, con solo sus miembros, el Objeto y todos los objetos a los que hace referencia (el gráfico de referencia). Podría referirse al tamaño en la memoria o al 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 (yo uso YourKit), que probablemente no es lo que quieres.

Sin embargo, de la descripción anterior parece que cada fila será independiente y no tendrá un gran árbol de dependencias, por lo que el método de serialización probablemente sea una buena aproximación en la mayoría de las JVM. La forma más fácil de hacerlo 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 lo hará 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.


56
2017-09-09 17:22



Accidentalmente encontré una clase de Java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", ya está 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

39
2017-09-09 07:53



Si solo desea saber cuánta memoria se está utilizando en su JVM y cuánto es gratuita, podría 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 utilizado 32 MB de memoria".


32
2017-09-09 17:19



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

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


17
2018-04-09 11:07



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

El proyecto jamm utiliza el paquete java.lang.instrumentation anterior, pero recorre el árbol y puede darle el uso de la memoria profunda.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm


13
2018-03-06 14:07



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

  • Solo la asignación de un objeto tiene cierta sobrecarga en la JVM. La cantidad varía según la JVM, por lo que puede convertir este valor en un parámetro. Al menos hazlo constante (¿8 bytes?) Y aplica a todo lo asignado.
  • Simplemente porque byte Teóricamente, 1 byte no significa que solo se necesita uno en la memoria.
  • Habrá bucles en referencias de objetos, por lo que deberá mantener un HashMap o somesuch usando object-equal como el comparador para eliminar bucles infinitos.

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


9
2017-09-09 17:19



Recomiendo la biblioteca java-sizeof para zanahsearch. Es muy simple.

Puedes obtenerlo en maven:

 <dependency>
    <groupId>com.carrotsearch</groupId>
    <artifactId>java-sizeof</artifactId>
    <version>0.0.3</version>
</dependency>

Es solo una línea de código que devuelve los bytes de un objeto:

RamUsageEstimator.sizeOf(new Object());

Puedes ver el código fuente en https://github.com/dweiss/java-sizeof

Y hay una presentación del autor de la biblioteca http://www.slideshare.net/DawidWeiss/sizeofobject-how-much-memory-objects-take-on-jvms-and-when-this-may-matter?ref=http://cheremin.blogspot.com/ 2012/05 / how-much-memory-objects-take-on-jvm-and.html


7
2017-11-23 12:14