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

StackOverflow https://stackoverflow.com/questions/52353

  •  09-06-2019
  •  | 
  •  

Pregunta

Por ejemplo, digamos que tengo una aplicación que puede leer 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 demasiadas filas de datos y causar OutOfMemoryErrors.Cada fila se traduce en un objeto.¿Existe una manera fácil de averiguar el tamaño de ese objeto mediante programación?¿Existe una referencia que defina qué tan grandes son los tipos primitivos y las referencias de objetos 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 32MB de la memoria.Quizás esa sea una pregunta diferente, pero aún así me gustaría saberlo.

¿Fue útil?

Solución

Puedes 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);
    }
}

Añade lo siguiente a tu MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Utilice 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

Otros consejos

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

JOL (Java Object Layout) es una pequeña caja de herramientas para analizar esquemas de diseño de objetos en JVM.Estas herramientas utilizan en gran medida Unsafe, JVMTI y Serviceability Agent (SA) para decodificar el diseño, la huella y las referencias reales del objeto.Esto hace que JOL sea mucho más preciso que otras herramientas que se basan en volcados de montón, suposiciones de especificaciones, etc.

Para obtener los tamaños de primitivas, referencias y elementos de matriz, utilice 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 sólo el espacio consumido por una única instancia de esa clase;no incluye ningún otro objeto al que haga referencia esa clase.Él hace incluya la sobrecarga de VM para el encabezado del objeto, la alineación del 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 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 recuperarse cuando ese objeto se recolecta como 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 de Pattern 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 lugar de eso usas GraphLayout.parseInstance(obj).toPrintable(), jol le indicará la dirección, el tamaño, el tipo, el valor y la ruta de las desreferencias de campo a cada objeto al que se hace referencia, aunque suele ser demasiado detalle para ser útil.Para el ejemplo de patrón continuo, es posible que obtenga 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 muestras jol en el repositorio de jol.Los ejemplos demuestran operaciones comunes de jol y muestran cómo puede utilizar jol para analizar los componentes internos de la máquina virtual y del recolector de basura.

Hace algunos años Javaworld tenía un artículo sobre la determinación del tamaño de objetos Java compuestos y potencialmente anidados, básicamente explican cómo crear una implementación sizeof() en Java.Básicamente, el enfoque se basa en otros trabajos en los que las personas identificaron experimentalmente el tamaño de los objetos primitivos y 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 será algo menos preciso que una implementación nativa de C simplemente por las cosas que suceden detrás de escena de una clase, pero debería ser un buen indicador.

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

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

En primer lugar, "el tamaño de un objeto" no es un concepto bien definido en Java.Podría referirse al objeto en sí, solo con 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 cadenas.

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

Sin embargo, según la descripción anterior, parece que cada fila será independiente y no tendrá un gran árbol de dependencia, 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 sencilla de hacerlo es la siguiente:

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

Recuerda que si tienes objetos con referencias comunes este no lo hará proporcione 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 de ByteArrayOutputStream a un valor razonable.

Accidentalmente encontré una clase Java "jdk.nashorn.internal.ir.debug.objectsizeCalculator", que 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

Si simplemente desea saber cuánta memoria se está utilizando en su JVM y cuánta está libre, 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 haber usado 32 MB de memoria".

Cuando trabajaba en Twitter, escribí una utilidad para calcular el tamaño de objetos profundos.Tiene en cuenta diferentes modelos de memoria (32 bits, comprimida, 64 bits), relleno, relleno de subclases, funciona correctamente en estructuras y matrices de datos circulares.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

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 sea lo que desea.

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

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

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

Tienes que recorrer los objetos usando la reflexión.Tenga cuidado al hacerlo:

  • La simple 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 conviértalo en una constante (¿8 bytes?) y aplíquelo a todo lo asignado.
  • Simplemente porque byte En teoría, es de 1 byte, no significa que solo se necesita uno en la memoria.
  • Habrá bucles en las referencias a objetos, por lo que necesitarás mantener un HashMap o algo así usando objetos iguales como 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.

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

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

  • booleano y byte:1 byte
  • Char y corto:2 bytes
  • int y flotante:4 bytes
  • largo y doble:8 bytes

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

Intentando hacerlo mediante programación con llamadas a Runtime.freeMemory() simplemente no le brinda mucha precisión debido a llamadas asincrónicas al recolector de basura, etc.Crear perfiles del montón con -Xrunhprof u otras herramientas le dará los resultados más precisos.

El java.lang.instrument.Instrumentation La clase proporciona una buena manera de obtener el tamaño de un objeto Java, pero requiere que definas un premain y ejecute su programa con un agente java.Esto es muy aburrido cuando no necesitas ningún agente y luego tienes que proporcionar un agente Jar ficticio a tu aplicación.

Entonces obtuve una solución alternativa usando el Unsafe clase de la 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 utilizo una clase auxiliar. UtilUnsafe para obtener una referencia a la sun.misc.Unsafe objeto.

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() { }
}

También está el Medidor de memoria herramienta (anteriormente en Código de Google, ahora en GitHub), que es simple y se publica bajo el formato comercial licencia apache 2.0, como se discutió en un 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 por lo demás parece funcionar bien, al menos en los escenarios que lo he usado.

Aquí hay una utilidad que creé usando algunos de los ejemplos vinculados para manejar 32 bits, 64 bits y 64 bits con programación orientada a objetos comprimida.Usa sun.misc.Unsafe.

Usa Unsafe.addressSize() para obtener el tamaño de un puntero nativo y Unsafe.arrayIndexScale( Object[].class ) para 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 );
    }
}

Sin tener que complicarse con la instrumentación y demás, y si no necesita saber el tamaño exacto en bytes de un objeto, puede optar por 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 al llamar 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 cuánta memoria requiere una ejecución.Aún más, puedes ejecutar todo más veces y hacer un promedio.

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

Si eso es demasiado lento o simplemente genera más problemas de los que vale la pena, siempre existe una buena regla general para contar filas a la antigua usanza.

Una vez escribí una prueba rápida 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 libre del montón.La clave es getFreeMemory(), cual solicita que se ejecute GC y espera a que se estabilice el tamaño del montón libre informado.El resultado 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 del 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 subproceso crea/descarta objetos.

Simplemente use Java Visual VM.

Tiene todo lo que necesitas 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)

Mi respuesta se basa en el código proporcionado por Nick.Ese código mide la cantidad total de bytes ocupados por el objeto serializado.Entonces, esto en realidad mide cosas de serialización + huella de memoria de objeto simple (solo serializar, por ejemplo). int y verá que la cantidad total de bytes serializados no es 4).Entonces, si desea que el número de bytes sin procesar se use 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;
    }    

}

Probé esta solución con tipos primitivos, String y algunas clases triviales.Es posible que también haya casos no cubiertos.


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

Podría generar un volcado de montón (con jmap, por ejemplo) y luego analizar el resultado para encontrar tamaños de objetos.Esta es una solución fuera de línea, pero puede examinar tamaños poco profundos y profundos, etc.

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 la jvm debido a la creación de objetos y, normalmente, ese es el tamaño del objeto.

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

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

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

  • (4/8 bytes) depende del sistema operativo (32/64 bits)

PRIMITIVOS

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 REFERENCIA 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á.

por ejemplo) Si creo un objeto para la siguiente clase ReferenceMemoryTest entonces se crearán 4 + 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 un objeto/matriz de 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 = nueva ReferenceMemoryTest();(4 (Ref1) + 12 = 16 bytes) ReferenceMeMemoryTest Ref2 = Ref1;(4(ref2) + 16 = 20 bytes)

Supongamos que declaro una clase llamada Complex como:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Para ver cuánta memoria está asignada a instancias activas de esta clase:

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

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex

Para JSONObject, el siguiente código puede ayudarle.

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

devuelve el tamaño en bytes

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

Dudo que quieras hacerlo mediante programación a menos que solo quieras hacerlo una vez y almacenarlo para uso futuro.Es algo costoso de hacer.No existe 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 el archivo en un archivo y observar 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 nada más.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top