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

1. Überblick

Im Gegensatz zu C/C, bei dem die sizeof _ () _ -Methode verwendet werden kann, um eine Objektgröße in Byte zu erhalten, gibt es in Java keine echte Entsprechung einer solchen Methode.

In diesem Artikel zeigen wir Ihnen, wie Sie die Größe eines bestimmten Objekts dennoch ermitteln können.

2. Speicherverbrauch in Java

Obwohl es in Java keinen sizeof -Operator gibt, brauchen wir eigentlich keinen. Alle Grundtypen haben eine Standardgröße, und normalerweise gibt es keine Auffüll- oder Ausrichtungsbytes. Dies ist jedoch nicht immer unkompliziert.

  • Obwohl Primitive sich so verhalten müssen, als hätten sie die offiziellen Größen, kann eine JVM Daten auf beliebige Weise intern speichern, mit beliebigem Füll- oder Zusatzaufwand ** . Es kann sich entscheiden, ein boolean[] ​​in 64-Bit-langen Abschnitten wie BitSet zu speichern, temporäre __Object __s auf dem Stack zuzuordnen oder einige Variablen oder Methodenaufrufe zu optimieren, die durch Konstanten usw. völlig verfehlt werden Da das Programm das gleiche Ergebnis liefert, ist es vollkommen in Ordnung.

Berücksichtigt man dabei auch die Auswirkungen auf die Hardware- und Betriebssystem-Caches (unsere Daten könnten auf jeder Cache-Ebene doppelt vorhanden sein), bedeutet dies, dass ** wir den RAM-Verbrauch nur ungefähr vorhersagen können.

2.1. Objekte, Referenzen und Wrapper-Klassen

  • Die minimale Objektgröße beträgt 16 Byte für ein modernes 64-Bit-JDK. ** Da das Objekt einen 12-Byte-Header hat, der auf ein Vielfaches von 8 Byte aufgefüllt wird. In 32-Bit-JDK beträgt der Overhead 8 Bytes, die auf ein Vielfaches von 4 Bytes aufgefüllt werden.

  • 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 über 32 GB.

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

Besonders relevant ist zu beachten, dass geschachtelte Typen, Arrays, _String s und andere Container wie mehrdimensionale Arrays speicherintensiv sind, da sie zusätzlichen Overhead verursachen. Wenn wir zum Beispiel int primitive (das nur 4 Bytes verbraucht) mit dem Integer_-Objekt vergleichen, das 16 Bytes benötigt, sehen wir, dass 300% Speicheraufwand vorhanden ist.

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

Eine Möglichkeit, die Größe eines Objekts in Java zu schätzen, ist die Verwendung der getObjectSize (Object) -Methode der Instrumentation interface in Java 5 eingeführt.

Wie wir in der Javadoc-Dokumentation sehen konnten, liefert die Methode eine "implementierungsspezifische Annäherung" der angegebenen Objektgröße.

Es ist bemerkenswert, dass ein potenzieller Einschluss von Overhead in der Größe vorhanden ist und die Werte während des Aufrufs einer einzelnen JVM abweichen können.

  • Dieser Ansatz unterstützt nur die Größenschätzung des betrachteten Objekts selbst und nicht die Größe der Objekte, auf die er 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 zu ermitteln, müssen wir zunächst auf die Instanz von Instrumentation zugreifen können. ** Wir müssen den Instrumentierungsagenten verwenden. Es gibt zwei Möglichkeiten, dies zu tun, wie in der Dokumentation zum https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package beschrieben -summary.html[ java.lang.instrument package.]

  • Der Instrumentation Agent 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 Sie die Implementierung der überladenen premain -Methode, die von der JVM bei Verwendung der Instrumentation zuerst aufgerufen wird. Außerdem müssen wir eine statische Methode verfügbar machen, um auf Instrumentation.getObjectSize (Object) zugreifen zu können.

Lassen Sie uns nun die InstrumentationAgent -Klasse erstellen:

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 einen Agenten-JAR mit der enthaltenen MANIFEST.MF-Datei 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<String> 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 die Option – javaagent in den Pfad zur Agenten-JAR aufnehmen, wenn Sie unsere Anwendung ausführen ** :

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

Die Ausgabe unserer Klasse zeigt 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. Schlussfolgerung

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

Den vollständigen Code zu diesem Artikel finden Sie wie immer im Projekt https://github.com/eugenp/tutorials/tree/master/core-java/