Frage Wie lässt sich die Größe eines Objekts in Java am besten ermitteln?


Angenommen, ich habe eine Anwendung, die eine CSV-Datei mit Datenzeilenstapeln lesen kann. Ich gebe dem Benutzer eine Zusammenfassung der Anzahl der Zeilen basierend auf den Datentypen, aber ich möchte sicherstellen, dass ich nicht zu viele Zeilen von Daten einlese und verursache OutOfMemoryErrors. Jede Zeile wird in ein Objekt umgesetzt. Gibt es eine einfache Möglichkeit, die Größe dieses Objekts programmgesteuert zu ermitteln? Gibt es eine Referenz, die definiert, wie groß primitive Typen und Objektreferenzen für a sind VM?

Gerade jetzt, ich habe Code, der sagt, bis zu lesen 32.000 Zeilen, aber ich hätte auch gerne Code, der besagt, dass ich so viele Zeilen wie möglich lesen soll, bis ich verwendet habe 32 MB der Erinnerung. Vielleicht ist das eine andere Frage, aber ich würde es trotzdem gerne wissen.


533
2017-09-09 17:07


Ursprung


Antworten:


Du kannst den ... benutzen java.lang.instrument Paket

Kompiliere und lege diese Klasse in ein 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);
    }
}

Fügen Sie Ihrem MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

Verwenden Sie getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Aufrufen mit:

java -javaagent:ObjectSizeFetcherAgent.jar C

403
2017-09-09 19:24



Vor einigen Jahren hatte Javaworld ein Artikel zur Bestimmung der Größe von zusammengesetzten und potentiell verschachtelten Java-ObjektenIm Grunde gehen sie durch das Erstellen einer sizeof () - Implementierung in Java. Der Ansatz baut im Wesentlichen auf anderen Arbeiten auf, bei denen die Größe von Primitiven und typischen Java-Objekten experimentell identifiziert und dann auf eine Methode angewendet wurde, die einen Objektgraphen rekursiv durchläuft, um die Gesamtgröße zu ermitteln.

Es wird immer etwas weniger genau als eine native C-Implementierung sein, einfach wegen der Dinge, die hinter den Kulissen einer Klasse ablaufen, aber es sollte ein guter Indikator sein.

Alternativ dazu wird ein SourceForge-Projekt entsprechend aufgerufen Größe von Das bietet eine Java5-Bibliothek mit einer sizeof () - Implementierung.

P.S. Verwenden Sie nicht den Serialisierungsansatz. Es gibt keine Korrelation zwischen der Größe eines serialisierten Objekts und der Menge an Speicher, die es verbraucht, wenn es live ist.


69
2017-09-09 18:42



Du solltest benutzen jol, ein Tool, das im Rahmen des OpenJDK-Projekts entwickelt wurde.

JOL (Java Object Layout) ist die kleine Toolbox zum Analysieren von Objektlayoutschemata in JVMs. Diese Tools verwenden Unsafe, JVMTI und den Serviceability Agent (SA), um das tatsächliche Objektlayout, den Footprint und die Referenzen zu dekodieren. Dies macht JOL viel genauer als andere Tools, die auf Heap-Dumps, Spezifikationsannahmen usw. beruhen.

Um die Größen von Primitiven, Referenzen und Array-Elementen zu erhalten, verwenden Sie VMSupport.vmDetails(). Auf Oracle JDK 1.8.0_40, das unter 64-Bit-Windows ausgeführt wird (für alle folgenden Beispiele), gibt diese Methode zurück

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]

Sie können die flache Größe einer Objektinstanz mit verwenden ClassLayout.parseClass(Foo.class).toPrintable() (Optional kann eine Instanz an toPrintable). Dies ist nur der Speicherplatz, der von einer einzelnen Instanz dieser Klasse belegt wird. Es enthält keine anderen Objekte, die von dieser Klasse referenziert werden. Es tut Fügen Sie den VM-Overhead für den Objektheader, die Feldausrichtung und das Padding hinzu. Zum 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

Sie können eine Übersicht über die Tiefe einer Objektinstanz mit erhalten GraphLayout.parseInstance(obj).toFootprint(). Natürlich können einige Objekte im Footprint freigegeben werden (auch von anderen Objekten referenziert), so dass es eine Überlappung des Speicherplatzes gibt, der zurückgewonnen werden könnte, wenn dieses Objekt als Garbage Collection erfasst wird. Für das Ergebnis von Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") (genommen von diese Antwort), berichtet Jol eine Gesamtfläche von 1840 Bytes, von denen nur 72 die Musterinstanz selbst sind.

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)

Wenn Sie stattdessen verwenden GraphLayout.parseInstance(obj).toPrintable()Jol wird Ihnen die Adresse, die Größe, den Typ, den Wert und den Pfad der Feld-Dereferenzierung zu jedem referenzierten Objekt mitteilen, obwohl das normalerweise zu detailliert ist, um nützlich zu sein. Für das Beispiel für ein laufendes Muster erhalten Sie möglicherweise Folgendes. (Adressen ändern sich wahrscheinlich zwischen den Läufen.)

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)

Die "(etwas anderes)" Einträge Beschreiben Sie andere Objekte im Heap, die nicht Teil dieses Objektdiagramms sind.

Die beste jol Dokumentation ist die jol Proben im Jol-Repository. Die Beispiele demonstrieren häufige jol-Vorgänge und zeigen, wie Sie mit jol VM- und Garbage Collector-Interna analysieren können.


60
2018-05-04 00:46



Erstens ist "die Größe eines Objekts" kein klar definiertes Konzept in Java. Sie könnten das Objekt selbst, mit seinen Mitgliedern, dem Objekt und allen Objekten, auf die es sich bezieht (das Referenzdiagramm), meinen. Sie könnten die Größe im Speicher oder die Größe auf der Festplatte meinen. Und die JVM darf Dinge wie Strings optimieren.

Der einzige richtige Weg ist es, die JVM mit einem guten Profiler zu fragen (ich benutze YourKit), was wahrscheinlich nicht das ist, was du willst.

Aus der obigen Beschreibung klingt jedoch, dass jede Zeile in sich abgeschlossen ist und keine große Abhängigkeitsstruktur aufweist, so dass die Serialisierungsmethode wahrscheinlich eine gute Annäherung an die meisten JVMs darstellt. Der einfachste Weg, dies zu tun, ist wie folgt:

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

Denken Sie daran, dass wenn Sie Objekte mit gemeinsamen Referenzen haben wird nicht Geben Sie das korrekte Ergebnis an, und die Größe der Serialisierung stimmt nicht immer mit der Größe im Speicher überein, aber es ist eine gute Annäherung. Der Code wird ein wenig effizienter, wenn Sie die ByteArrayOutputStream-Größe auf einen sinnvollen Wert initialisieren.


56
2017-09-09 17:22



Ich habe zufällig eine Java-Klasse gefunden "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", bereits in jdk, das ist einfach zu bedienen und scheint sehr nützlich für die Bestimmung der Größe eines Objekts.

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

Ergebnisse:

164192
48
16
48
416

39
2017-09-09 07:53



Wenn Sie nur wissen möchten, wie viel Speicher in Ihrer JVM verwendet wird und wie viel frei ist, können Sie Folgendes versuchen:

// 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();

edit: Ich dachte, das könnte hilfreich sein, da der Fragesteller auch erklärt, dass er eine Logik haben möchte, die "so viele Zeilen wie möglich liest, bis ich 32 MB Speicher verwendet habe".


32
2017-09-09 17:19



Damals, als ich bei Twitter gearbeitet habe, habe ich ein Programm geschrieben, mit dem ich die Tiefe von Objekten berechnen kann. Es berücksichtigt verschiedene Speichermodelle (32-Bit, komprimierte Ups, 64-Bit), Padding, Unterklassen-Padding, funktioniert auf zirkulären Datenstrukturen und Arrays korrekt. Sie können nur diese eine .java Datei kompilieren; Es hat keine externen Abhängigkeiten:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java


17
2018-04-09 11:07