setsize - En Java, ¿cuál es la mejor manera de determinar el tamaño de un objeto?
swing java (24)
Por ejemplo, digamos que tengo una aplicación que puede leer un archivo CSV con pilas de filas de datos. Le doy al usuario un resumen de la cantidad de filas según los tipos de datos, pero quiero asegurarme de que no leo demasiadas filas de datos y que causo OutOfMemoryError
s. Cada fila se traduce en un objeto. ¿Hay 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 una 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 todavía me gustaría saber.
Cuando trabajé en Twitter, escribí una utilidad para calcular el tamaño de un 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. Puedes simplemente compilar este archivo .java; No tiene dependencias externas.
Debe medirlo con una herramienta o estimarlo a mano, y depende de la JVM que esté utilizando.
Hay algunos gastos generales fijos por objeto. Es específico de JVM, pero por lo general calculo 40 bytes. Entonces tienes que mirar a los miembros de la clase. Las referencias de 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 se multiplica 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 asíncronas al recolector de basura, etc. Perfilar el montón con -Xrunhprof u otras herramientas le dará los resultados más precisos.
Debe 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 están utilizando Inseguro, JVMTI y Serviceability Agent (SA) en gran medida para descodificar el diseño real del objeto, la huella y las referencias. Esto hace que JOL sea mucho más preciso que otras herramientas que se basan en volcados de pila, suposiciones de especificación, 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 utilizando 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. Incluye la sobrecarga de la máquina virtual 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 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 se hace referencia a otros objetos), por lo que es una sobre aproximación del espacio que podría reclamarse cuando ese objeto se recolecta como 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.
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, utiliza GraphLayout.parseInstance(obj).toPrintable()
, jol le dirá la dirección, el tamaño, el tipo, el valor y la ruta de las referencias a los campos a los que se hace referencia, aunque eso suele ser demasiado detallado para ser útil. Para el ejemplo de patrón en curso, 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 muestran las operaciones jol comunes y muestran cómo puede usar jol para analizar los aspectos internos del recolector de basura y la máquina virtual.
En primer lugar, "el tamaño de un objeto" no es un concepto bien definido en Java. Podría significar el 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 significar el tamaño en la memoria o el tamaño en el disco. Y a la JVM se le permite optimizar cosas como Strings.
Entonces, la única forma correcta es preguntar a la JVM, con un buen generador de perfiles (yo uso YourKit ), que probablemente no sea lo que quieres.
Sin embargo, a partir 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 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 de 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.
Encontré accidentalmente una clase 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
Hace algunos años, Javaworld tenía un artículo sobre la determinación del tamaño de los objetos Java compuestos y potencialmente anidados , que básicamente se basan en la creación de una implementación sizeof () en Java. Básicamente, el enfoque se basa en otro trabajo donde las personas identificaron experimentalmente el tamaño de los primitivos y los objetos Java típicos y luego aplican ese conocimiento a un método que recursivamente recorre un gráfico de objetos para calcular 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 llamado apropiadamente 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á vivo.
La clase java.lang.instrument.Instrumentation
proporciona una buena manera de obtener el tamaño de un objeto Java, pero requiere que premain
una premain
y premain
tu programa con un agente java. Esto es muy aburrido cuando no necesita ningún agente y luego debe proporcionar un agente Jar ficticio a su aplicación.
Así que obtuve una solución alternativa utilizando la clase Unsafe
de sun.misc
. Por lo tanto, considerando la alineación del montón de objetos de acuerdo con la arquitectura del procesador y calculando el desplazamiento máximo del campo, puede medir el tamaño de un objeto Java. En el siguiente ejemplo, uso una clase auxiliar UtilUnsafe
para obtener una referencia al objeto sun.misc.Unsafe
.
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() { }
}
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 no es lo que probablemente desea.
El proyecto jamm utiliza el paquete java.lang.instrumentation anterior, pero recorre el árbol y, por lo tanto, puede proporcionarle un uso profundo de la memoria.
new MemoryMeter().measureDeep(myHashMap);
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
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
Recomiendo la biblioteca java-sizeof para la carrotsearch
de carrotsearch
. Es muy simple.
Puedes conseguirlo en maven:
<dependency>
<groupId>com.carrotsearch</groupId>
<artifactId>java-sizeof</artifactId>
<version>0.0.3</version>
</dependency>
Solo una línea de código devuelve los bytes de un objeto:
RamUsageEstimator.sizeOf(new Object());
Puedes ver el código fuente en Github
Y here hay una presentación del autor de la biblioteca.
Si solo desea saber cuánta memoria se está utilizando en su JVM y cuánta es gratuita, puede probar 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();
Edición: pensé que esto podría ser útil ya que la pregunta del autor también dijo que le gustaría tener una lógica que maneje "leer tantas filas como sea posible hasta que haya usado 32 MB de memoria".
Tienes que caminar los objetos utilizando la reflexión. Ten cuidado al hacer:
- 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 hacer que este valor sea un parámetro. Al menos, conviértalo en una constante (¿8 bytes?) Y aplíquelo a cualquier cosa asignada.
- Solo porque el
byte
es 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
HashMap
o algo así como el uso de object-igual como el comparador para eliminar los bucles infinitos.
@jodonnell: Me gusta la simplicidad de su solución, pero muchos objetos no se pueden serializar (por lo que esto generaría una excepción), los campos pueden ser transitorios y los objetos pueden anular los métodos estándar.
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 que GC se ejecute y espere a que se estabilice el tamaño de pila libre reportado . 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 almacenamiento dinámico.
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 en condiciones controladas donde ningún otro subproceso está creando / descartando objetos.
Para JSONObject el siguiente código puede ayudarte.
`JSONObject.toString().getBytes("UTF-8").length`
devuelve el tamaño en bytes
Lo comprobé con mi objeto JSONArray escribiéndolo en un archivo. Está dando el tamaño del objeto.
Sólo tiene que utilizar Java VM visual.
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)
Sin tener que meterse con la instrumentación, etc., y si no necesita saber el tamaño exacto de un objeto, puede utilizar 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 usted lee la memoria usada antes y después, y al llamar al GC justo antes de obtener la memoria usada, baja el "ruido" casi a 0.
Para 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 se necesita para una ejecución. Aún más, puedes correrlo todo más veces y hacer un promedio.
Aquí hay una utilidad que hice usando algunos de los ejemplos vinculados para manejar 32 bits, 64 bits y 64 bits con OOP comprimido. Utiliza sun.misc.Unsafe
.
Se utiliza 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 );
}
}
Dudo que quiera hacerlo programáticamente a menos que solo quiera hacerlo una vez y almacenarlo para uso futuro. Es una cosa costosa de hacer. No hay 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 los primitivos.
Una forma en que podría hacerlo es serializar la cosa en un archivo y ver el tamaño del archivo, de esta manera:
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 ninguna otra cosa.
Otra estrategia sería tomar cada objeto y examinar sus miembros por reflexión y sumar los tamaños (booleano y byte = 1 byte, short & char = 2 bytes, etc.), avanzando hacia abajo 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.
Esta respuesta no está relacionada con el tamaño del objeto, pero cuando se usa una matriz para acomodar los objetos; la cantidad de memoria que asignará para el 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 el tamaño real de la memoria de objetos), solo almacenará referencias para esos objetos.
Ahora el Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
- (4/8 bytes) depende del sistema operativo (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 doble referencia de objeto, pero dependiendo de la creación del objeto, la memoria necesaria variará.
por ejemplo) Si creo un objeto para la siguiente clase, ReferenceMemoryTest
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 una matriz de objetos / referencias, 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)
Mi respuesta se basa en el código proporcionado por Nick. Ese código mide la cantidad total de bytes que ocupa el objeto serializado. Por lo tanto, esto mide realmente la huella de la serialización + la huella de la memoria del objeto simple (solo serializar, por ejemplo, int
y verá que la cantidad total de bytes serializados no lo es 4
). Por lo tanto, si desea obtener el número de byte 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 matriz.
No hay una llamada al método, si eso es lo que estás pidiendo. Con un poco de investigación, supongo que podrías escribir el tuyo. Una instancia particular tiene un tamaño fijo derivado del número de referencias y valores primitivos más datos de contabilidad de instancia. Simplemente caminarías el gráfico de objetos. Cuanto menos variados sean los tipos de fila, más fácil será.
Si eso es demasiado lento o simplemente más problema de lo que vale, siempre hay una buena regla de recuento de filas a la antigua.
Podría generar un volcado de pila (con jmap, por ejemplo) y luego analizar la salida 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.
También está la herramienta Memory Measurer (anteriormente en Google Code , ahora en GitHub ), que es simple y se publica bajo la licencia Apache 2.0 , que es fácil de usar y comercial , como se explica en una pregunta similar .
También requiere un argumento de línea de comandos para el intérprete 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.
long heapSizeBefore = Runtime.getRuntime().totalMemory();
// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;
tamaño le da el aumento en el uso de memoria del jvm debido a la creación del objeto y que normalmente es el tamaño del objeto