Come ottenere le dimensioni di un oggetto in Java

1. Panoramica

A differenza di C / C ++ dove possiamo usare il metodo sizeof () per ottenere una dimensione dell'oggetto in byte, non esiste un vero equivalente di tale metodo in Java.

In questo articolo, dimostreremo come possiamo ancora ottenere le dimensioni di un particolare oggetto.

2. Consumo di memoria in Java

Sebbene non ci sia un operatore sizeof in Java, in realtà non ne abbiamo bisogno. Tutti i tipi primitivi hanno una dimensione standard e in genere non ci sono pad o byte di allineamento. Tuttavia, questo non è sempre semplice.

Sebbene le primitive debbano comportarsi come se avessero le dimensioni ufficiali, una JVM può memorizzare i dati in qualsiasi modo preferisca internamente, con qualsiasi quantità di riempimento o sovraccarico . Può scegliere di memorizzare un booleano [] in blocchi lunghi 64 bit come BitSet , allocare alcuni oggetti temporanei sullo stack o ottimizzare alcune variabili o chiamate di metodi completamente fuori dall'esistenza sostituendole con costanti, ecc ... Ma, fintanto che il il programma dà lo stesso risultato, va benissimo.

Considerando anche l'impatto dell'hardware e delle cache del sistema operativo (i nostri dati potrebbero essere duplicati a ogni livello di cache), significa che possiamo prevedere solo approssimativamente il consumo di RAM .

2.1. Oggetti, riferimenti e classi wrapper

La dimensione minima dell'oggetto è di 16 byte per il moderno JDK a 64 bit poiché l'oggetto ha un'intestazione di 12 byte, riempita con un multiplo di 8 byte. In JDK a 32 bit, l'overhead è di 8 byte, riempito con un multiplo di 4 byte.

I riferimenti hanno una dimensione tipica di 4 byte su piattaforme a 32 bit e su piattaforme a 64 bit con limite di heap inferiore a 32 GB ( -Xmx32G ) e 8 byte per questo limite superiore a 32 GB.

Ciò significa che una JVM a 64 bit richiede solitamente il 30-50% di spazio in più nell'heap.

Particolarmente rilevante è notare che i tipi in box, gli array, le stringhe e altri contenitori come gli array multidimensionali sono costosi per la memoria poiché aggiungono un certo sovraccarico . Ad esempio, quando confrontiamo la primitiva int (che consuma solo 4 byte) con l' oggetto Integer che richiede 16 byte, vediamo che c'è un overhead di memoria del 300%.

3. Stima delle dimensioni degli oggetti utilizzando la strumentazione

Un modo per ottenere una stima della dimensione di un oggetto in Java è utilizzare il metodo getObjectSize (Object) dell'interfaccia di strumentazione introdotto in Java 5.

Come abbiamo potuto vedere nella documentazione Javadoc, il metodo fornisce "un'approssimazione specifica dell'implementazione" della dimensione dell'oggetto specificato. È interessante notare che esiste una potenziale inclusione di overhead nelle dimensioni e che i valori possono essere diversi durante la singola chiamata JVM.

Questo approccio supporta solo la stima delle dimensioni dell'oggetto considerato stesso e non le dimensioni degli oggetti a cui fa riferimento . Per stimare una dimensione totale dell'oggetto, avremmo bisogno di un codice che superasse quei riferimenti e calcolasse la dimensione stimata.

3.1. Creazione dell'agente di strumentazione

Per chiamare Instrumentation.getObjectSize (Object) per ottenere la dimensione dell'oggetto, dobbiamo prima essere in grado di accedere all'istanza di Instrumentation. Dobbiamo usare l'agente di strumentazione e ci sono due modi per farlo, come descritto nella documentazione per il pacchetto java.lang.instrument .

L'agente di strumentazione può essere specificato tramite la riga di comando oppure è possibile utilizzarlo con una JVM già in esecuzione . Ci concentreremo sul primo.

Per specificare l'agente di strumentazione tramite la riga di comando , avremo bisogno dell'implementazione del metodo premain sovraccarico che verrà richiamato per primo dalla JVM quando si utilizza la strumentazione. Oltre a ciò, dobbiamo esporre un metodo statico per poter accedere a Instrumentation.getObjectSize (Object) .

Creiamo ora la classe InstrumentationAgent :

public class InstrumentationAgent { private static volatile Instrumentation globalInstrumentation; public static void premain(final String agentArgs, final Instrumentation inst) { globalInstrumentation = inst; } public static long getObjectSize(final Object object) { if (globalInstrumentation == null) { throw new IllegalStateException("Agent not initialized."); } return globalInstrumentation.getObjectSize(object); } }

Prima di creare un JAR per questo agente, dobbiamo assicurarci che un semplice metafile, MANIFEST.MF sia incluso in esso :

Premain-class: com.baeldung.objectsize.InstrumentationAgent

Ora possiamo creare un JAR agente con il file MANIFEST.MF incluso. Un modo è tramite riga di comando:

javac InstrumentationAgent.java jar cmf MANIFEST.MF InstrumentationAgent.jar InstrumentationAgent.class

3.2. Classe di esempio

Vediamolo in azione creando una classe con oggetti di esempio che utilizzeranno la nostra classe agente:

public class InstrumentationExample { public static void printObjectSize(Object object) { System.out.println("Object type: " + object.getClass() + ", size: " + InstrumentationAgent.getObjectSize(object) + " bytes"); } public static void main(String[] arguments) { String emptyString = ""; String string = "Estimating Object Size Using Instrumentation"; String[] stringArray = { emptyString, string, "com.baeldung" }; String[] anotherStringArray = new String[100]; List stringList = new ArrayList(); StringBuilder stringBuilder = new StringBuilder(100); int maxIntPrimitive = Integer.MAX_VALUE; int minIntPrimitive = Integer.MIN_VALUE; Integer maxInteger = Integer.MAX_VALUE; Integer minInteger = Integer.MIN_VALUE; long zeroLong = 0L; double zeroDouble = 0.0; boolean falseBoolean = false; Object object = new Object(); class EmptyClass { } EmptyClass emptyClass = new EmptyClass(); class StringClass { public String s; } StringClass stringClass = new StringClass(); printObjectSize(emptyString); printObjectSize(string); printObjectSize(stringArray); printObjectSize(anotherStringArray); printObjectSize(stringList); printObjectSize(stringBuilder); printObjectSize(maxIntPrimitive); printObjectSize(minIntPrimitive); printObjectSize(maxInteger); printObjectSize(minInteger); printObjectSize(zeroLong); printObjectSize(zeroDouble); printObjectSize(falseBoolean); printObjectSize(Day.TUESDAY); printObjectSize(object); printObjectSize(emptyClass); printObjectSize(stringClass); } public enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } }

Affinché funzioni, dobbiamo includere l' opzione - javaagent con il percorso dell'agente JAR durante l'esecuzione della nostra applicazione :

VM Options: -javaagent:"path_to_agent_directory\InstrumentationAgent.jar"

L'output dell'esecuzione della nostra classe ci mostrerà le dimensioni degli oggetti stimate:

Object type: class java.lang.String, size: 24 bytes Object type: class java.lang.String, size: 24 bytes Object type: class [Ljava.lang.String;, size: 32 bytes Object type: class [Ljava.lang.String;, size: 416 bytes Object type: class java.util.ArrayList, size: 24 bytes Object type: class java.lang.StringBuilder, size: 24 bytes Object type: class java.lang.Integer, size: 16 bytes Object type: class java.lang.Integer, size: 16 bytes Object type: class java.lang.Integer, size: 16 bytes Object type: class java.lang.Integer, size: 16 bytes Object type: class java.lang.Long, size: 24 bytes Object type: class java.lang.Double, size: 24 bytes Object type: class java.lang.Boolean, size: 16 bytes Object type: class com.baeldung.objectsize.InstrumentationExample$Day, size: 24 bytes Object type: class java.lang.Object, size: 16 bytes Object type: class com.baeldung.objectsize.InstrumentationExample$1EmptyClass, size: 16 bytes Object type: class com.baeldung.objectsize.InstrumentationExample$1StringClass, size: 16 bytes

4. Conclusione

In questo articolo, abbiamo descritto come la memoria viene utilizzata da particolari tipi in Java, come JVM memorizza i dati e abbiamo sottolineato le cose che possono influire sul consumo totale di memoria. Abbiamo quindi dimostrato come possiamo in pratica ottenere la dimensione stimata degli oggetti Java.

Come sempre, il codice completo relativo a questo articolo si trova nel progetto GitHub.