So ermitteln Sie die Größe eines Objekts in Java

1. Übersicht

Im Gegensatz zu C / C ++, wo wir die sizeof () -Methode verwenden können, um eine Objektgröße in Bytes zu erhalten, gibt es in Java kein echtes Äquivalent einer solchen Methode.

In diesem Artikel zeigen wir, wie wir immer noch die Größe eines bestimmten Objekts ermitteln können.

2. Speicherverbrauch in Java

Obwohl es in Java keinen Operator sizeof gibt , benötigen wir keinen. Alle primitiven Typen haben eine Standardgröße, und normalerweise gibt es keine Pad- oder Alignment-Bytes. Dies ist jedoch nicht immer einfach.

Obwohl sich Grundelemente so verhalten müssen, als hätten sie die offiziellen Größen, kann eine JVM Daten nach Belieben intern speichern, mit beliebigem Auffüllen oder Overhead . Es kann wählen, ob ein Boolescher Wert [] in 64-Bit-langen Blöcken wie BitSet gespeichert , einige temporäre Objekte auf dem Stapel zugewiesen oder einige Variablen oder Methodenaufrufe optimiert werden sollen, die nicht mehr existieren, und sie durch Konstanten usw. ersetzen sollen Programm gibt das gleiche Ergebnis, es ist vollkommen in Ordnung.

Berücksichtigt man auch die Auswirkungen der Hardware- und Betriebssystem-Caches (unsere Daten können auf jeder Cache-Ebene dupliziert werden), können wir den RAM-Verbrauch nur grob vorhersagen .

2.1. Objekte, Referenzen und Wrapper-Klassen

Die minimale Objektgröße beträgt 16 Byte für modernes 64-Bit-JDK, da das Objekt einen 12-Byte-Header hat, der auf ein Vielfaches von 8 Bytes aufgefüllt ist. Im 32-Bit-JDK beträgt der Overhead 8 Byte, aufgefüllt mit einem Vielfachen von 4 Byte.

Referenzen haben eine typische Größe von 4 Byte auf 32-Bit-Plattformen und auf 64-Bit-Plattformen mit einer Heap-Grenze von weniger als 32 GB ( -Xmx32G ) und 8 Byte für diese Grenze von mehr als 32 GB.

Dies bedeutet, dass eine 64-Bit-JVM normalerweise 30-50% mehr Heap-Speicherplatz benötigt.

Besonders relevant ist, dass Boxed-Typen, Arrays, Strings und andere Container wie mehrdimensionale Arrays speicherintensiv sind, da sie einen gewissen Overhead verursachen . Wenn wir beispielsweise das Grundelement int (das nur 4 Bytes verbraucht) mit dem Integer- Objekt vergleichen, das 16 Bytes benötigt, sehen wir, dass 300% Speicher-Overhead vorhanden sind.

3. Schätzen der Objektgröße mithilfe von Instrumenten

Eine Möglichkeit, eine Schätzung der Größe eines Objekts in Java zu erhalten, ist die Verwendung der Methode getObjectSize (Object) der in Java 5 eingeführten Instrumentation- Schnittstelle.

Wie wir in der Javadoc-Dokumentation sehen konnten, bietet die Methode eine „implementierungsspezifische Annäherung“ an die Größe des angegebenen Objekts. Es ist bemerkenswert, dass möglicherweise ein Overhead in die Größe einbezogen wird und die Werte während eines einzelnen JVM-Aufrufs unterschiedlich sein können.

Dieser Ansatz unterstützt nur die Größenschätzung des betrachteten Objekts selbst und nicht die Größe der Objekte, auf die es verweist . Um die Gesamtgröße des Objekts zu schätzen, benötigen wir einen Code, der diese Referenzen durchläuft und die geschätzte Größe berechnet.

3.1. Instrumentation Agent erstellen

Um Instrumentation.getObjectSize (Object) aufzurufen , um die Objektgröße abzurufen, müssen wir zuerst auf die Instanz von Instrumentation zugreifen können. Wir müssen den Instrumentierungsagenten verwenden, und es gibt zwei Möglichkeiten, dies zu tun, wie in der Dokumentation zum Paket java.lang.instrument beschrieben .

Der Instrumentierungsagent kann über die Befehlszeile angegeben werden oder wir können ihn mit einer bereits laufenden JVM verwenden . Wir werden uns auf den ersten konzentrieren.

Um den Instrumentierungsagenten über die Befehlszeile anzugeben , benötigen wir die Implementierung der überladenen Premain- Methode, die bei Verwendung der Instrumentierung zuerst von der JVM aufgerufen wird. Außerdem müssen wir eine statische Methode verfügbar machen, um auf Instrumentation.getObjectSize (Object) zugreifen zu können .

Erstellen wir nun die InstrumentationAgent- Klasse:

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

Bevor wir eine JAR für diesen Agenten erstellen, müssen wir sicherstellen, dass eine einfache Metadatei, MANIFEST.MF, darin enthalten ist :

Premain-class: com.baeldung.objectsize.InstrumentationAgent

Jetzt können wir eine Agent-JAR mit der enthaltenen Datei MANIFEST.MF erstellen. Eine Möglichkeit ist über die Befehlszeile:

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

3.2. Beispielklasse

Lassen Sie uns dies in Aktion sehen, indem Sie eine Klasse mit Beispielobjekten erstellen, die unsere Agentenklasse verwenden:

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 } }

Damit dies funktioniert, müssen wir beim Ausführen unserer Anwendung die Option - javaagent mit dem Pfad zur Agenten-JAR einschließen :

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

Die Ausgabe der Ausführung unserer Klasse zeigt uns die geschätzten Objektgrößen:

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. Fazit

In diesem Artikel haben wir beschrieben, wie der Speicher von bestimmten Typen in Java verwendet wird, wie JVM Daten speichert und Dinge hervorgehoben, die sich auf den gesamten Speicherverbrauch auswirken können. Wir haben dann gezeigt, wie wir in der Praxis die geschätzte Größe von Java-Objekten ermitteln können.

Wie immer finden Sie den vollständigen Code zu diesem Artikel im GitHub-Projekt.