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

617

Ich habe eine Anwendung, die eine CSV-Datei mit Stapel von Datenzeilen liest. Ich gebe dem Benutzer eine Zusammenfassung der Anzahl der Zeilen basierend auf Datentypen, aber ich möchte sicherstellen, dass ich nicht zu viele Datenzeilen einlese und OutOfMemoryErrors verursache . Jede Zeile wird in ein Objekt übersetzt. 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?

Im Moment habe ich Code, der besagt, dass bis zu 32.000 Zeilen gelesen werden sollen , aber ich möchte auch Code, der besagt, dass so viele Zeilen wie möglich gelesen werden, bis ich 32 MB Speicher verwendet habe. Vielleicht ist das eine andere Frage, aber ich würde es trotzdem gerne wissen.

Jay R.
quelle
Ich fügte meinen Agenten mit mvn configs hinzu und erklärte, wie hier: stackoverflow.com/a/36102269/711855
juanmf

Antworten:

461

Sie können das Paket java.lang.instrument verwenden

Kompilieren Sie diese Klasse und fügen Sie sie in ein JAR ein:

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 Folgendes hinzu 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()));
    }
}

Rufen Sie auf mit:

java -javaagent:ObjectSizeFetcherAgent.jar C
Stefan Karlsson
quelle
2
@Stefan Netter Hinweis! Können Sie mir bitte sagen, was die Größe sein wird byte[0], byte[1], byte[5], int[0], int[1], int[2]den Ansatz mit Ihnen beschrieben? Es wäre schön, wenn die Ergebnisse Overhead für die Länge des Arrays und die Speicherausrichtung enthalten würden.
dma_k
8
Ich habe es versucht und seltsame und nicht hilfreiche Ergebnisse erzielt. Die Saiten waren immer 32, unabhängig von der Größe. Ich dachte, dies wäre vielleicht die Zeigergröße, aber für eine andere unveränderliche Klasse, die ich erstellt habe, habe ich 24. Es funktioniert gut für Grundelemente, aber dann brauchen Sie kein Programm, um zu sagen, wie groß ein Zeichen ist.
Brel
6
@Brel Diese Lösung ist nur eine "Annäherung an die vom angegebenen Objekt verbrauchte Speichermenge", wie in der Dokumentation angegeben. Ich nehme auch an, dass die Autoren beschlossen haben, die Größe eines Strings aufgrund des String-Pools von Java auf 32 Byte (nur den Zeiger?) Festzulegen, was es schwierig macht zu sagen, ob eine String-Instanz gemeinsam genutzt (im Pool gespeichert) oder lokal und einzigartig für eine Klasse.
Andrei I
11
Wie kann ich ObjectSizeFetcher verwenden, wenn kein JAR exportiert wird? Ich habe Java-Testprojekt in Eclipse.
Yura Shinkarev
3
@brel Der Grund, warum ein String unabhängig von der tatsächlichen Länge nur 32 Byte beträgt, liegt darin, dass der Teil eines Strings mit variabler Länge in einem char [] gespeichert ist, bei dem es sich um ein eigenes Objekt handelt. Um die wahre Größe eines Objekts zu erhalten, müssen Sie die Größe von sich selbst und die Größe jedes Objekts hinzufügen, auf das es verweist.
Tombrown52
117

Sie sollten jol verwenden , ein Tool, das im Rahmen des OpenJDK-Projekts entwickelt wurde.

JOL (Java Object Layout) ist die winzige Toolbox zum Analysieren von Objektlayoutschemata in JVMs. Diese Tools verwenden Unsafe, JVMTI und Serviceability Agent (SA) in hohem Maße, 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. basieren.

Verwenden Sie, um die Größe von Grundelementen, Referenzen und Array-Elementen zu ermitteln VMSupport.vmDetails(). Unter Oracle JDK 1.8.0_40 unter 64-Bit-Windows (für alle folgenden Beispiele verwendet) wird diese Methode zurückgegeben

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 geringe Größe einer Objektinstanz mithilfe von ClassLayout.parseClass(Foo.class).toPrintable()(optional Übergabe einer Instanz an toPrintable) ermitteln. Dies ist nur der Speicherplatz, der von einer einzelnen Instanz dieser Klasse belegt wird. Es enthält keine anderen Objekte, auf die diese Klasse verweist. Es enthält VM-Overhead für den Objektheader, die Feldausrichtung und das Auffüllen. Für 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

Mit können Sie eine zusammenfassende Ansicht der Tiefengröße einer Objektinstanz erhalten GraphLayout.parseInstance(obj).toFootprint(). Natürlich können einige Objekte im Footprint gemeinsam genutzt werden (auch von anderen Objekten referenziert), sodass es sich um eine Überannäherung des Speicherplatzes handelt, der zurückgefordert werden könnte, wenn dieses Objekt durch Müll gesammelt wird. Für das Ergebnis von Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")(aus dieser Antwort entnommen) meldet jol einen Gesamt-Footprint von 1840 Bytes, von denen nur 72 die Pattern-Instanz 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(), teilt Ihnen jol die Adresse, Größe, den Typ, den Wert und den Pfad der Felddereferenzen zu jedem referenzierten Objekt mit, obwohl dies normalerweise zu detailliert ist, um nützlich zu sein. Für das laufende Musterbeispiel erhalten Sie möglicherweise Folgendes. (Die Adressen werden sich wahrscheinlich zwischen den Läufen ändern.)

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 Einträge "(etwas anderes)" beschreiben andere Objekte im Heap, die nicht Teil dieses Objektdiagramms sind .

Die beste JOL-Dokumentation sind die JOL-Beispiele im JOL-Repository. Die Beispiele zeigen allgemeine JOL-Operationen und zeigen, wie Sie JOL verwenden können, um VM- und Garbage Collector-Interna zu analysieren.

Jeffrey Bosboom
quelle
18
Diese Antwort sollte mehr positive Stimmen haben. Auf jeden Fall eine sehr gute Option zu überprüfen. EDIT: Überprüft, ob dies in diesem Jahr hinzugefügt wurde, während die Frage in '08 gestellt wurde. Wahrscheinlich die beste und einfachste Option, um das zu tun, was OP im Moment verlangt hat.
mietet
4
Der Tool-Autor hat einen Blog-Beitrag über Jol geschrieben .
Mike
2
Um die Größe des Objekts "obj" zu bestimmen, verwenden Sie: org.openjdk.jol.info.GraphLayout.parseInstance (obj) .totalSize ();
Kraft
Beachten Sie, dass vmDetailsdas jetzt ist VM.current().details().
Miha_x64
Check out GraphLayout.parseInstance(instance).toFootprint()Ich fand es nützlicher, Objektgrößen zu verstehen
Mugen
82

Ich habe versehentlich eine Java-Klasse "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator" gefunden, die sich bereits in jdk befindet und einfach zu verwenden ist und für die Bestimmung der Größe eines Objekts sehr nützlich erscheint.

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
Tom
quelle
3
Gleich hier habe ich die anderen oben vorgeschlagenen Lösungen ausprobiert und bin auf ObjectSizeCalculator gestoßen. Ich glaube, niemand hat dies zuvor erwähnt, da es kürzlich im Rahmen des Projekts Nashorn auf dem JDK 8 eingeführt wurde . Ich habe jedoch keine offizielle Dokumentation zu dieser Klasse im Internet gefunden.
Henrique Gontijo
Es scheint keine Stringlängen zu berücksichtigen. Geht es nur um die Größe auf dem Stapel?
Jontejj
1
Ich habe eine Hashmap, in der com.carrotsearch.RamUsageEstimator ungefähr die Hälfte von ObjectSizeCalculator zurückgibt. Welches ist wahr? - Welches ist zuverlässiger?
Badera
9
Beachten Sie, dass ObjectSizeCalculatornur auf HotSpot VM unterstützt wird
kellanburket
74

Vor einigen Jahren hatte Javaworld einen Artikel über die Bestimmung der Größe von zusammengesetzten und möglicherweise verschachtelten Java-Objekten veröffentlicht. Im Grunde geht es darum, eine sizeof () -Implementierung in Java zu erstellen. Der Ansatz baut im Wesentlichen auf anderen Arbeiten auf, bei denen die Größe von Grundelementen und typischen Java-Objekten experimentell identifiziert und dieses Wissen dann auf eine Methode angewendet wird, die rekursiv einen Objektgraphen durchläuft, um die Gesamtgröße zu ermitteln.

Es wird immer etwas weniger genau sein als eine native C-Implementierung, nur weil sich hinter den Kulissen einer Klasse etwas abspielt, aber es sollte ein guter Indikator sein.

Alternativ ein SourceForge-Projekt mit dem passenden Namen sizeof , das eine Java5-Bibliothek mit einer sizeof () -Implementierung bietet.

PS Verwenden Sie nicht den Serialisierungsansatz. Es besteht keine Korrelation zwischen der Größe eines serialisierten Objekts und der Menge an Speicher, die es im Live-Betrieb verbraucht.

Boris Terzic
quelle
6
Die Größe des Dienstprogramms ist wahrscheinlich der schnellste Weg. Es ist im Grunde das, was Stefan gesagt hat, aber bereits in einem gebrauchsfertigen Glas verpackt.
Alexandre L Telles
62

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

Der einzig richtige Weg ist also, die JVM mit einem guten Profiler (ich benutze YourKit ) zu fragen , was wahrscheinlich nicht das ist, was Sie wollen.

Aus der obigen Beschreibung geht jedoch hervor, dass jede Zeile in sich geschlossen ist und keinen großen Abhängigkeitsbaum aufweist. Daher ist die Serialisierungsmethode bei den meisten JVMs wahrscheinlich eine gute Annäherung. 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 Objekte mit allgemeinen Referenzen nicht das richtige Ergebnis liefern und die Größe der Serialisierung nicht immer mit der Größe im Speicher übereinstimmt, dies ist jedoch eine gute Annäherung. Der Code ist etwas effizienter, wenn Sie die ByteArrayOutputStream-Größe auf einen sinnvollen Wert initialisieren.

Nick Fortescue
quelle
2
Ich mag diesen Ansatz. Wie weit waren Sie in Bezug auf die Objektgröße entfernt?
Berlin Brown
1
Sehr einfach und effektiv. Andere Methoden sind zu chaotisch (insbesondere innerhalb von Eclipse RCP). Vielen Dank.
marcolopes
19
Die Serialisierung verfolgt keine vorübergehenden Variablen, und die Standard-Serialisierungsmethode schreibt Zeichenfolgen in UTF-8, sodass ANSI-Zeichen nur ein Byte benötigen. Wenn Sie viele Zeichenfolgen haben, ist Ihre Größe so weit entfernt, dass sie unbrauchbar ist.
TMN
1
Obwohl dies möglicherweise nicht die genaue Größe angibt, benötigte ich für meine Anforderungen nur einen Vergleich zwischen 2 Objekten und SizeOf wird nicht über eine Web-App initialisiert. Vielen Dank!
Isaac
1
Gute Empfehlung von YourKit . Andere Alternativen sind VirtualVM und jvmmonitor
angelcervera
38

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

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

Bearbeiten: Ich dachte, dies könnte hilfreich sein, da der Autor der Frage auch erklärte, er hätte gerne eine Logik, die "so viele Zeilen wie möglich lesen, bis ich 32 MB Speicher verwendet habe" handhabt.

matt b
quelle
24
Dies ist keine gute Lösung, da Sie nie wissen, wann eine Speicherbereinigung stattfinden wird oder wie viel zusätzlicher Speicher dem Heap gleichzeitig zugewiesen wird.
Nick Fortescue
5
Das ist wahr, und ich würde nicht beabsichtigen, die Hauptfrage dieses Beitrags zu beantworten, aber es könnte ihm helfen, programmgesteuert zu wissen, wann er sich dem Erreichen der maximalen Heap-Größe nähert.
Matt b
1
Ein weiteres Problem dieser Lösung besteht darin, dass Sie sich in einer Multithread-Umgebung befinden (wie auf einem Webserver). Es ist möglich, dass andere Threads ausgeführt wurden und Speicher beanspruchten. Mit dieser Näherung berechnen Sie den verwendeten Speicher in der gesamten virtuellen Maschine.
Angelcervera
1
Ein weiterer Nachteil ist, dass freeMemory eine Annäherung zurückgibt. Versuchen Sie, ein javax.crypto.Cipher-Objekt zu erstellen. Der Unterschied zwischen zwei Aufrufen von freeMemory (um die Größe einer Chiffre zu schätzen) ist nicht konstant!
Eugen
1
Ich glaube, Sie können eine Speicherbereinigung erzwingen, damit Sie einige Dinge in diesem Ansatz tun können .
Matanster
24

Als ich bei Twitter arbeitete, schrieb ich ein Dienstprogramm zur Berechnung der Größe großer Objekte. Es berücksichtigt verschiedene Speichermodelle (32-Bit, komprimiertes Hoppla, 64-Bit), Auffüllen, Auffüllen von Unterklassen und funktioniert korrekt bei kreisförmigen Datenstrukturen und Arrays. Sie können einfach diese eine .java-Datei kompilieren. Es gibt keine externen Abhängigkeiten:

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

Attila Szegedi
quelle
1
Szia! Ich möchte auch nur Ihre Präsentation hervorheben: Die Folien 15 bis 20 sind großartig, um ein instinktives Gefühl für die Kosten verschiedener Datenstrukturentscheidungen zu bekommen. Danke, dass du das gepostet hast!
Luke Usherwood
16
"es hat keine externen Abhängigkeiten" - seit wann ist Guave keine externe Abhängigkeit?
14 MPI
Guave ist eine externe Abhängigkeit.
Mert Serimer
18

Viele der anderen Antworten bieten flache Größen - z. B. die Größe einer HashMap ohne einen der Schlüssel oder Werte, was wahrscheinlich nicht das ist, was Sie wollen.

Das Jamm-Projekt verwendet das obige Paket java.lang.instrumentation, geht jedoch über den Baum und kann Ihnen so die Verwendung von Deep Memory ermöglichen.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Starten Sie die JVM mit "-javaagent: /jamm.jar", um MemoryMeter zu verwenden.

Reich
quelle
11

Sie müssen die Objekte mit Reflexion gehen. Seien Sie vorsichtig wie Sie:

  • Das Zuweisen eines Objekts hat in der JVM einen gewissen Overhead. Der Betrag variiert je nach JVM, sodass Sie diesen Wert möglicherweise zu einem Parameter machen. Machen Sie es mindestens zu einer Konstanten (8 Bytes?) Und gelten Sie für alles, was zugewiesen ist.
  • Nur weil bytetheoretisch 1 Byte ist, heißt das nicht, dass nur eines im Speicher benötigt wird.
  • Es wird Schleifen in Objektreferenzen geben, daher müssen Sie eine HashMapoder mehrere Objektgleichungen als Komparator verwenden , um Endlosschleifen zu eliminieren.

@jodonnell: Ich mag die Einfachheit Ihrer Lösung, aber viele Objekte sind nicht serialisierbar (dies würde also eine Ausnahme auslösen), Felder können vorübergehend sein und Objekte können die Standardmethoden überschreiben.

Jason Cohen
quelle
Sind die Größen verschiedener Grundelemente nicht in der Java-Spezifikation definiert? (§2.4.1)
erickson
4
Nicht im Sinne von "wie viel Speicher belegt es", das ist die Frage. Nur im Sinne ihrer Funktionsweise. Zum Beispiel nehmen Bytes, Zeichen und Kurzschlüsse ein ganzes Wort auf dem Java-Stapel ein, obwohl sie mit Rundungen usw. arbeiten.
Jason Cohen
1
Dies klingt ähnlich wie das Messen der Größe, wie Heinz in seinem Newsletter Nr. 78 gezeigt hat: javaspecialists.eu/archive/Issue078.html . Ich benutzte es. Sein Ansatz funktioniert.
Peter Kofler
8

Sie müssen es mit einem Tool messen oder von Hand schätzen. Dies hängt von der verwendeten JVM ab.

Es gibt einen festen Overhead pro Objekt. Es ist JVM-spezifisch, aber ich schätze normalerweise 40 Bytes. Dann müssen Sie sich die Mitglieder der Klasse ansehen. Objektreferenzen sind 4 (8) Bytes in einer 32-Bit (64-Bit) -JVM. Primitive Typen sind:

  • Boolescher Wert und Byte: 1 Byte
  • char und short: 2 Bytes
  • int und float: 4 Bytes
  • lang und doppelt: 8 Bytes

Arrays folgen denselben Regeln. Das heißt, es ist eine Objektreferenz, die 4 (oder 8) Bytes in Ihrem Objekt benötigt und dann seine Länge multipliziert mit der Größe seines Elements.

Der Versuch, dies programmgesteuert mit Aufrufen zu tun Runtime.freeMemory(), führt aufgrund asynchroner Aufrufe des Garbage Collector usw. nicht zu einer hohen Genauigkeit. Wenn Sie den Heap mit -Xrunhprof oder anderen Tools profilieren, erhalten Sie die genauesten Ergebnisse.

erickson
quelle
@erickson Ich wäre mir nicht sicher, ob sizeof (boolean) == 1 diesen Thread betrachtet ( stackoverflow.com/questions/1907318/… ). Können Sie dies bitte kommentieren?
dma_k
2
@dma_k, Java hat eigentlich keine echten Booleschen Werte. Die Größe des Booleschen Werts beträgt 4 Byte außerhalb von Arrays und 1 Byte innerhalb von Arrays boolean[]. Tatsächlich sind alle Primitiven, die keine doppelten / langen Typen sind, 4 Bytes. Letztere sind 8 (die Antwort setzt sie fälschlicherweise auch als 4)
bestsss
@bestsss: Genauer gesagt hängt die minimale Speicherzuweisung von der Plattform und der Implementierung von JVM ab. Auch Objekte auf dem Haufen werden ausgerichtet, sodass nach dem Aufsummieren aller Größen aufgerundet werden muss.
dma_k
6

Die java.lang.instrument.InstrumentationKlasse bietet eine gute Möglichkeit, die Größe eines Java-Objekts zu ermitteln. Sie müssen jedoch ein definieren premainund Ihr Programm mit einem Java-Agenten ausführen. Dies ist sehr langweilig, wenn Sie keinen Agenten benötigen und dann Ihrer Anwendung einen Dummy-Jar-Agenten zur Verfügung stellen müssen.

Also habe ich eine alternative Lösung mit der UnsafeKlasse von der sun.misc. Wenn Sie also die Ausrichtung des Objektheaps gemäß der Prozessorarchitektur berücksichtigen und den maximalen Feldversatz berechnen, können Sie die Größe eines Java-Objekts messen. Im folgenden Beispiel verwende ich eine Hilfsklasse UtilUnsafe, um einen Verweis auf das sun.misc.UnsafeObjekt zu erhalten.

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() { }
}
Miguel Gamboa
quelle
Interessanter Ansatz, aber setzt dies nicht voraus, dass das Objekt und sein Feldspeicher nicht fragmentiert sind?
Nicoulaj
Ja, und ich kenne keine JVM-Implementierung, die eine solche Fragmentierung bewirkt.
Miguel Gamboa
Ich verstehe nicht Fragmentierung ist keine Option :) Nehmen wir das Beispiel von Objekt C, das als Feld der Objekte A und B gespeichert ist. Verschiebt es nicht das Ganze in A oder B?
Nicoulaj
Entschuldigung, aber ich verstehe auch Ihren Standpunkt nicht. Nach meiner Interpretation können in Java Objekte nicht in anderen Objekten gespeichert werden, wie dies bei C-Strukturen oder Werttypen in .Net der Fall ist. Wenn Sie also sagen: "Objekt C, das als Feld der Objekte A und B gespeichert ist", bedeutet dies, dass die Objekte A und B Felder haben, in denen Verweise (Zeiger) auf das Objekt C gespeichert sind. Dann ist die Größe von A und B gleich der Versatz dieses Feldes plus die Größe einer Referenz (Zeiger) auf das Objekt C. Und die Größe einer Referenz ist die Größe eines Wortes.
Miguel Gamboa
Oh, OK, wir sprechen von geringer Größe. Mein Fehler.
Nicoulaj
6

Es gibt auch das Memory Measurer- Tool (früher bei Google Code , jetzt bei GitHub ), das einfach ist und unter der kommerziell freundlichen Apache 2.0-Lizenz veröffentlicht wird , wie in einer ähnlichen Frage erläutert .

Es erfordert auch ein Befehlszeilenargument für den Java-Interpreter, wenn Sie den Speicherbyteverbrauch messen möchten, aber ansonsten scheint es zumindest in den von mir verwendeten Szenarien gut zu funktionieren.

PNS
quelle
4

Ohne sich mit Instrumenten usw. herumschlagen zu müssen und wenn Sie die bytegenaue Größe eines Objekts nicht kennen müssen, können Sie den folgenden Ansatz wählen:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Auf diese Weise lesen Sie den verwendeten Speicher vorher und nachher und rufen den GC auf, bevor Sie den verwendeten Speicher erhalten, und senken das "Rauschen" fast auf 0.

Für ein zuverlässigeres Ergebnis können Sie Ihren Job n-mal ausführen und dann den verwendeten Speicher durch n teilen, um zu erhalten, wie viel Speicher ein Lauf benötigt. Darüber hinaus können Sie das Ganze mehrmals ausführen und einen Durchschnitt erstellen.

wirklich nett
quelle
5
System.gc()Benachrichtigt nicht nur, dass Sie GC möchten? Es kann nicht garantiert werden, dass der GC überhaupt aufgerufen wird.
Raildex
@wirklich nett. Dies ist nicht sicher, da Sie möglicherweise nie wissen, was GC tut oder den Speicher zwischen Ihren Leitungen beeinflusst. "Zwischen" zwei freeMemory-Methoden GC kann also mehr Speicherplatz freigeben, den Sie nicht berücksichtigen, sodass Ihr Objekt kleiner aussieht
Mert Serimer
@MertSerimer "nicht sicher" ist für mich auf einer ganz anderen Ebene: höchstens ist dies nicht so genau, wie ich auch sagte. Sie können den GC auch nicht fahren (wie von Raildex angegeben), aber auch für diesen Fall habe ich vorgeschlagen, dies in einen Zyklus einzufügen. Dies ist nur ein schnelles, schmutziges und ungefähres System, das funktioniert, wenn das Ergebnis nicht wie angegeben sehr zuverlässig sein muss.
wirklich schön
Es gibt viele Probleme damit, aber es gibt Ihnen einen guten Swag.
Markthegrea
3

Hier ist ein Dienstprogramm, das ich mithilfe einiger der verknüpften Beispiele erstellt habe, um 32-Bit, 64-Bit und 64-Bit mit komprimiertem OOP zu verarbeiten. Es verwendet sun.misc.Unsafe.

Es wird verwendet Unsafe.addressSize(), um die Größe eines nativen Zeigers und Unsafe.arrayIndexScale( Object[].class )die Größe einer Java-Referenz abzurufen.

Es verwendet den Feldversatz einer bekannten Klasse, um die Basisgröße eines Objekts zu ermitteln.

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 );
    }
}
dlaudams
quelle
Haben Sie diese Klasse mit Werten getestet? Ich habe versucht, aber für mich falsche Werte !!!.
Débora
1
Die Werte, die es mir für ein einfaches Objekt gab, waren ungefähr korrekt, aber um den Faktor 10 für eine Liste mit 1 Million Objekten. Trotzdem sehr gute Arbeit!
Michael Böckling
Interessant. Ich habe es mit JDK7u67 unter Windows 7 x64 und Linux 2.6.16 / x86_64 mit jedem der 32-Bit / 64-Bit / OOP-Adressmodi getestet. Ich habe es mit Speicherauszügen verglichen, die in Eclipse Memory Analyzer 1.3.x analysiert wurden. Welches Setup verwenden Sie? Haben Sie ein konkretes Beispiel, das ich ausprobieren könnte?
Dlaudams
Beste Wahl, die ich machen kann. Ich kann nicht verwenden, Instrumentationweil ich Tomcat ObjectSizeCalculatornicht JOLstarte , weil ich nicht sicher bin , ob es sich um einen VM-Typ (HotSpot) handelt oder ob es sich um Bacouse Spring Beans handelt. Ich benutze dies und füge einen zweiten Parameter hinzu, um Singletons zu ignorieren, nämlich den AbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()Refactor- internalSizeOfCode, um Class und Enum zu ignorieren
Perlos
Verwenden Sie zum Vergleichen der Ergebnisse ObjectSizeCalculator (Berechnen Sie den gesamten Server mit 1 GB bis 10 s). JOL verursachen MemError (6 GB sind nicht ausreichend) und ich erhalte nicht die gleichen Ergebnisse, wahrscheinlich weil Aufzählungen.
Perlos
3

Ich suchte nach einer Laufzeitberechnung einer Objektgröße, die die folgenden Anforderungen erfüllt:

  • Verfügbar zur Laufzeit ohne Instrumentierung.
  • Funktioniert mit Java 9+ ohne Zugriff auf Unsafe.
  • Basiert nur auf der Klasse. Keine tiefe Größe, die Zeichenfolgenlängen, Arraylängen usw. berücksichtigt.

Das Folgende basiert auf dem Kerncode des ursprünglichen Artikels von Java-Spezialisten ( https://www.javaspecialists.eu/archive/Issue078.html ) und einigen Bits aus der unsicheren Version in einer anderen Antwort auf diese Frage.

Ich hoffe, jemand findet es nützlich.

public class JavaSize {

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 HEADER_SIZE = 8;

public static int sizeOf(Class<?> clazz) {
    int result = 0;

    while (clazz != null) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if (!Modifier.isStatic(fields[i].getModifiers())) {
                if (fields[i].getType().isPrimitive()) {
                    Class<?> primitiveClass = fields[i].getType();
                    if (primitiveClass == boolean.class || primitiveClass == byte.class) {
                        result += 1;
                    } else if (primitiveClass == short.class) {
                        result += 2;
                    } else if (primitiveClass == int.class || primitiveClass == float.class) {
                        result += 4;
                    } else if (primitiveClass == double.class || primitiveClass == long.class) {
                        result += 8;
                    }

                } else {
                    // assume compressed references.
                    result += 4;
                }
            }
        }

        clazz = clazz.getSuperclass();

        // round up to the nearest WORD length.
        if ((result % WORD) != 0) {
            result += WORD - (result % WORD);
        }
    }

    result += HEADER_SIZE;

    return result;
}

}}

David Ryan
quelle
2

Es gibt keinen Methodenaufruf, wenn Sie danach fragen. Mit ein wenig Recherche könnten Sie wahrscheinlich Ihre eigenen schreiben. Eine bestimmte Instanz hat eine feste Größe, die aus der Anzahl der Referenzen und Grundwerte sowie den Buchhaltungsdaten der Instanz abgeleitet wird. Sie würden einfach das Objektdiagramm durchlaufen. Je weniger die Zeilentypen variieren, desto einfacher.

Wenn das zu langsam ist oder nur mehr Mühe als es wert ist, gibt es immer gute, altmodische Daumenzählregeln.

sblundy
quelle
2

Ich habe einmal einen Schnelltest geschrieben, um im Handumdrehen abzuschätzen:

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

}

Das allgemeine Konzept besteht darin, Objekte zuzuweisen und Änderungen im freien Heapspeicher zu messen. Der Schlüssel ist getFreeMemory(), dass GC-Läufe angefordert werden und auf die Stabilisierung der gemeldeten freien Heap-Größe gewartet wird . Die Ausgabe der oben genannten ist:

nested:        160000 each=16
static nested: 160000 each=16

Welches ist, was wir angesichts des Ausrichtungsverhaltens und des möglichen Overheads des Heapblock-Headers erwarten.

Die in der akzeptierten Antwort angegebene Instrumentierungsmethode ist hier die genaueste. Die von mir beschriebene Methode ist genau, jedoch nur unter kontrollierten Bedingungen, unter denen keine anderen Threads Objekte erstellen / verwerfen.

Jason C.
quelle
2

Verwenden Sie einfach Java Visual VM.

Es enthält alles, was Sie zum Profilieren und Debuggen von Speicherproblemen benötigen.

Es hat auch eine OQL-Konsole (Object Query Language), mit der Sie viele nützliche Dinge tun können, darunter eine sizeof(o)

ACV
quelle
2

Wenn Sie JetBrains IntelliJ verwenden, aktivieren Sie zuerst "Speicheragenten anhängen" in Datei | Einstellungen | Erstellen, Ausführen, Bereitstellen | Debugger.

Klicken Sie beim Debuggen mit der rechten Maustaste auf eine interessierende Variable und wählen Sie "Aufbewahrte Größe berechnen": Berechnen Sie die beibehaltene Größe

simon04
quelle
1

Meine Antwort basiert auf dem von Nick bereitgestellten Code. Dieser Code misst die Gesamtmenge der Bytes, die vom serialisierten Objekt belegt werden. Dies misst also tatsächlich das Serialisierungsmaterial + den einfachen Objektspeicher-Footprint (zum Beispiel nur serialisieren intund Sie werden sehen, dass die Gesamtmenge der serialisierten Bytes nicht vorhanden ist 4). Wenn Sie also eine Rohbyte-Nummer erhalten möchten, die genau für Ihr Objekt verwendet wird, müssen Sie diesen Code ein wenig ändern. Wie so:

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

}

Ich habe diese Lösung mit primitiven Typen, String und einigen trivialen Klassen getestet. Es kann auch nicht abgedeckte Fälle geben.


UPDATE: Beispiel geändert, um die Berechnung des Speicherbedarfs von Array-Objekten zu unterstützen.

Agnius Vasiliauskas
quelle
0

Sie können einen Heap-Dump erstellen (z. B. mit jmap) und dann die Ausgabe analysieren, um die Objektgrößen zu ermitteln. Dies ist eine Offline-Lösung, aber Sie können flache und tiefe Größen usw. untersuchen.

JZeeb
quelle
0
long heapSizeBefore = Runtime.getRuntime().totalMemory();

// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

Größe gibt Ihnen die Zunahme der Speichernutzung des JVM aufgrund der Objekterstellung und das ist in der Regel die Größe des Objekts.

user835199
quelle
Was ist, wenn GC während // Code für die Objektkonstruktion in der Mitte ausgeführt wird? Könnte jetzt immer das richtige Ergebnis liefern.
Rajugaadu
0

Diese Antwort bezieht sich nicht auf die Objektgröße, sondern wenn Sie ein Array verwenden, um die Objekte aufzunehmen. Wie viel Speichergröße wird dem Objekt zugewiesen?

Arrays, Listen oder Zuordnungen all dieser Sammlungen werden also nicht wirklich Objekte speichern (nur zum Zeitpunkt der Grundelemente wird eine echte Objektspeichergröße benötigt), sondern nur Referenzen für diese Objekte.

Jetzt die Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8 Bytes) hängt vom Betriebssystem (32/64 Bit) ab

PRIMITIVE

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

OBJEKTE

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.

Ich möchte damit sagen, dass das gesamte Objekt REFERENCE nur 4 Byte Speicher benötigt. Es kann sich um eine Zeichenfolgenreferenz oder eine doppelte Objektreferenz handeln. Abhängig von der Objekterstellung variiert der benötigte Speicher.

zB) Wenn ich ein Objekt für die unten stehende Klasse erstelle, werden ReferenceMemoryTest4 + 4 + 4 = 12 Bytes Speicher erstellt. Der Speicher kann unterschiedlich sein, wenn Sie versuchen, die Referenzen zu initialisieren.

 class ReferenceMemoryTest {
    public String refStr;
    public Object refObj;
    public Double refDoub; 
}

Wenn Sie also ein Objekt- / Referenzarray erstellen, wird der gesamte Inhalt mit NULL-Referenzen belegt. Und wir wissen, dass jede Referenz 4 Bytes benötigt.

Und schließlich beträgt die Speicherzuordnung für den folgenden Code 20 Bytes.

ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 Bytes) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 Bytes)

Kanagavelu Sugumar
quelle
1
Wie können eine 4-Byte-Ganzzahl und eine Objektreferenz unbekannter Größe in 4 Byte passen?
Marquis von Lorne
@EJP Ich meine zu sagen, dass das gesamte Objekt REFERENCE nur 4 Bytes Speicher benötigt. Es kann sich um eine Zeichenfolgenreferenz oder eine doppelte Objektreferenz handeln. Abhängig von der Objekterstellung variiert der benötigte Speicher.
Kanagavelu Sugumar
0

Angenommen, ich deklariere eine Klasse mit dem Namen Complex:

public class Complex {

    private final long real;
    private final long imaginary;

    // omitted
}

Um zu sehen, wie viel Speicher Live-Instanzen dieser Klasse zugewiesen ist:

$ jmap -histo:live <pid> | grep Complex

 num     #instances         #bytes  class name (module)
-------------------------------------------------------
 327:             1             32  Complex
Ali Dehghani
quelle
-5

Für JSONObject kann Ihnen der folgende Code helfen.

`JSONObject.toString().getBytes("UTF-8").length`

Gibt die Größe in Bytes zurück

Ich habe es mit meinem JSONArray-Objekt überprüft, indem ich es in eine Datei geschrieben habe. Es gibt die Objektgröße an.

Solokiran
quelle
Dies würde nur für Objekte funktionieren, bei denen es sich hauptsächlich um Zeichenfolgen handelt.
Dexter Legaspi
-6

Ich bezweifle, dass Sie es programmgesteuert ausführen möchten, es sei denn, Sie möchten es nur einmal ausführen und für die zukünftige Verwendung aufbewahren. Es ist eine kostspielige Sache. In Java gibt es keinen sizeof () -Operator, und selbst wenn dies der Fall wäre, würden nur die Kosten für die Verweise auf andere Objekte und die Größe der Grundelemente berücksichtigt.

Eine Möglichkeit besteht darin, das Objekt in eine Datei zu serialisieren und die Größe der Datei wie folgt zu überprüfen:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

Dies setzt natürlich voraus, dass jedes Objekt unterschiedlich ist und keine nicht vorübergehenden Verweise auf etwas anderes enthält.

Eine andere Strategie wäre, jedes Objekt zu nehmen und seine Mitglieder durch Reflexion zu untersuchen und die Größen (Boolescher Wert & Byte = 1 Byte, kurz & Zeichen = 2 Byte usw.) zu addieren, um sich in der Mitgliedschaftshierarchie nach unten zu arbeiten. Aber das ist mühsam und teuer und macht am Ende das Gleiche wie die Serialisierungsstrategie.

Jodonnell
quelle
3
Ich würde es mit einem ByteArrayOutputStream in ein Byte [] serialisieren. Es wäre viel schneller als das Schreiben in eine Datei.
ScArcher2
@KorayTugay Das Bestimmen der Bytegröße eines Objekts ist bereits eine kostspielige Operation. Wenn Sie jedes Objekt auf die Festplatte schreiben, um die Größe zu bestimmen, wird es nur kriechen ...
HammerNL
1
Das serialisierte Objektformat unterscheidet sich vollständig vom Format des Objekts im Heapspeicher. Insbesondere wird ein Deskriptor für die Klasse des Objekts (und alle seine serialisierbaren Oberklassen) in den Stream geschrieben. Das Schreiben einer einfachen Instanz von java.lang.Integererzeugt also ungefähr 80 Bytes, wobei die Heap-Darstellung normalerweise 32 beträgt (im Gegensatz zur Objektstromdarstellung hängt die Heap-Darstellung von der Zeigergröße und der Objektausrichtung ab). Im Gegensatz dazu nullbenötigt eine serialisierte Referenz ein Byte anstelle der vier oder acht Bytes im Heapspeicher.
Holger