Frage

Nehmen wir zum Beispiel an, ich habe eine Anwendung, die eine CSV-Datei mit Stapeln von Datenzeilen einlesen kann.Ich gebe dem Benutzer eine Zusammenfassung der Anzahl der Zeilen basierend auf den Datentypen, möchte aber sicherstellen, dass ich nicht zu viele Datenzeilen und Ursachen einlese OutOfMemoryErrorS.Jede Zeile wird in ein Objekt übersetzt.Gibt es eine einfache Möglichkeit, die Größe dieses Objekts programmgesteuert herauszufinden?Gibt es eine Referenz, die definiert, wie groß primitive Typen und Objektreferenzen für a sind? VM?

Im Moment habe ich Code mit der Aufschrift „Bis lesen bis“. 32.000 Zeilen, aber ich hätte auch gerne Code, der besagt, dass so viele Zeilen wie möglich gelesen werden, bis ich sie verwendet habe 32 MB der Erinnerung.Vielleicht ist das eine andere Frage, aber ich würde es trotzdem gerne wissen.

War es hilfreich?

Lösung

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

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

Aufrufen mit:

java -javaagent:ObjectSizeFetcherAgent.jar C

Andere Tipps

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

JOL (Java Object Layout) ist die kleine Toolbox zur Analyse von Objektlayoutschemata in JVMs.Diese Tools verwenden in großem Umfang Unsafe, JVMTI und Serviceability Agent (SA), um das tatsächliche Objektlayout, den Footprint und die Referenzen zu dekodieren.Dadurch ist JOL viel genauer als andere Tools, die auf Heap-Dumps, Spezifikationsannahmen usw. basieren.

Um die Größe von Grundelementen, Referenzen und Array-Elementen zu ermitteln, verwenden Sie VMSupport.vmDetails().Unter Oracle JDK 1.8.0_40, das unter 64-Bit-Windows ausgeführt wird (wird für alle folgenden Beispiele verwendet), 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 geringe Größe einer Objektinstanz mit ermitteln ClassLayout.parseClass(Foo.class).toPrintable() (optional Übergabe einer Instanz an toPrintable).Dies ist nur der Speicherplatz, der von einer einzelnen Instanz dieser Klasse beansprucht wird.Es enthält keine anderen Objekte, auf die diese Klasse verweist.Es tut Dazu gehören VM-Overhead für den Objektheader, die Feldausrichtung und die Auffüllung.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 (und auch von anderen Objekten referenziert werden), es handelt sich also um eine übermäßige Annäherung an den Platz, der zurückgewonnen 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-.]+$") (genommen von diese Antwort), meldet jol einen Gesamt-Footprint von 1840 Bytes, wovon nur 72 auf die Pattern-Instanz selbst entfallen.

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 teilt Ihnen die Adresse, Größe, den Typ, den Wert und den Pfad der Felddereferenzen zu jedem referenzierten Objekt mit, obwohl das normalerweise zu viele Details enthält, um nützlich zu sein.Für das fortlaufende Musterbeispiel erhalten Sie möglicherweise Folgendes.(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 „(etwas anderes)“-Einträge Beschreiben Sie andere Objekte im Heap, die nicht Teil dieses Objektdiagramms sind.

Die beste Jol-Dokumentation ist die Jol-Beispiele im JOL-Repository.Die Beispiele veranschaulichen gängige JOL-Vorgänge und zeigen, wie Sie JOL zum Analysieren von VM- und Garbage Collector-Interna verwenden können.

Vor einigen Jahren hatte Javaworld ein Artikel zur Bestimmung der Größe zusammengesetzter und möglicherweise verschachtelter Java-Objekte, gehen sie im Grunde genommen durch die Erstellung einer sizeof()-Implementierung in Java.Der Ansatz baut im Wesentlichen auf anderen Arbeiten auf, bei denen Menschen experimentell die Größe von Grundelementen und typischen Java-Objekten ermittelten und dieses Wissen dann auf eine Methode anwenden, die einen Objektgraphen rekursiv durchläuft, um die Gesamtgröße zu ermitteln.

Es wird aufgrund der Dinge, die sich hinter den Kulissen einer Klasse abspielen, immer etwas ungenauer sein als eine native C-Implementierung, aber es sollte ein guter Indikator sein.

Alternativ kann ein SourceForge-Projekt entsprechend aufgerufen werden Größe von das eine Java5-Bibliothek mit einer sizeof()-Implementierung bietet.

P.S.Verwenden Sie nicht den Serialisierungsansatz, da keine Korrelation zwischen der Größe eines serialisierten Objekts und der Menge an Speicher besteht, die es im aktiven Zustand verbraucht.

Erstens ist „die Größe eines Objekts“ in Java kein genau definiertes Konzept.Sie könnten das Objekt selbst meinen, nur mit seinen Mitgliedern, das Objekt und alle Objekte, auf die es verweist (das Referenzdiagramm).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 einzig richtige Weg besteht also darin, die JVM mit einem guten Profiler (ich verwende) zu fragen YourKit), 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, sodass die Serialisierungsmethode bei den meisten JVMs wahrscheinlich eine gute Annäherung 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, wenn Sie Objekte mit gemeinsamen Referenzen haben wird nicht liefert das richtige Ergebnis, 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 etwas effizienter, wenn Sie die ByteArrayOutputStream-Größe auf einen sinnvollen Wert initialisieren.

Ich habe versehentlich eine Java -Klasse "Jdk.nashorn.internal.ir.Debug.Objectsizecalculator" gefunden, die bereits in JDK verwendet wird und die zur Bestimmung der Größe eines Objekts leicht zu verwenden ist und sehr nützlich zu sein scheint.

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

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

bearbeiten:Ich dachte, das könnte hilfreich sein, da der Autor der Frage auch angegeben hat, dass er gerne eine Logik hätte, die „so viele Zeilen wie möglich liest, bis ich 32 MB Speicher verbraucht habe“.

Als ich bei Twitter arbeitete, habe ich ein Dienstprogramm zur Berechnung der Größe tiefer Objekte geschrieben.Es berücksichtigt verschiedene Speichermodelle (32-Bit, komprimierte Hoppla, 64-Bit), Padding und Unterklassen-Padding und funktioniert korrekt mit kreisförmigen Datenstrukturen und Arrays.Sie können einfach 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

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

Das Jamm-Projekt verwendet das oben genannte java.lang.instrumentation-Paket, durchläuft jedoch den Baum und kann Ihnen so eine umfassende Speichernutzung ermöglichen.

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

Um MemoryMeter zu verwenden, starten Sie die JVM mit „-javaagent:/jamm.jar“.

Sie müssen die Objekte mithilfe von Reflexion durchlaufen.Seien Sie dabei vorsichtig:

  • Allein die Zuweisung eines Objekts verursacht einen gewissen Mehraufwand in der JVM.Der Betrag variiert je nach JVM, daher können Sie diesen Wert als Parameter festlegen.Machen Sie es zumindest zu einer Konstante (8 Bytes?) und wenden Sie sie auf alles an, was zugewiesen wird.
  • Nur weil byte theoretisch 1 Byte beträgt, bedeutet nicht, dass nur eines im Speicher benötigt wird.
  • Es wird Schleifen in Objektreferenzen geben, daher müssen Sie eine behalten HashMap oder so Verwenden von object-equals als Komparator um Endlosschleifen zu beseitigen.

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

Sie müssen es mit einem Werkzeug messen oder von Hand schätzen, und es hängt von der JVM ab, die Sie verwenden.

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 kurz:2 Bytes
  • int und float:4 Bytes
  • lang und doppelt:8 Byte

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

Ich versuche es programmgesteuert mit Aufrufen von zu tun Runtime.freeMemory() Aufgrund asynchroner Aufrufe an den Garbage Collector usw. erhalten Sie einfach keine große Genauigkeit.Das Profilieren des Heaps mit -Xrunhprof oder anderen Tools liefert die genauesten Ergebnisse.

Der java.lang.instrument.Instrumentation Die Klasse bietet eine gute Möglichkeit, die Größe eines Java-Objekts zu ermitteln, erfordert jedoch die Definition eines premain und führen Sie Ihr Programm mit einem Java-Agenten aus.Das ist sehr langweilig, wenn Sie keinen Agenten benötigen und Ihrer Anwendung dann einen Dummy-Jar-Agenten bereitstellen müssen.

Also habe ich eine alternative Lösung mit dem gefunden Unsafe Klasse aus der sun.misc.Wenn Sie also die Heap-Ausrichtung der Objekte entsprechend 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 darauf zu erhalten sun.misc.Unsafe Objekt.

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

Es gibt auch die Speichermesser Werkzeug (früher bei Google-Code, jetzt weiter GitHub), die einfach ist und unter dem Motto „Werbefreundlich“ veröffentlicht wird Apache 2.0-Lizenz, wie in a besprochen ähnliche Frage.

Es erfordert ebenfalls ein Befehlszeilenargument für den Java-Interpreter, wenn Sie den Speicherbyte-Verbrauch messen möchten, aber ansonsten scheint es einwandfrei zu funktionieren, zumindest in den Szenarien, in denen ich es verwendet habe.

Hier ist ein Dienstprogramm, das ich anhand einiger der verlinkten Beispiele erstellt habe, um 32-Bit, 64-Bit und 64-Bit mit komprimiertem OOP zu verarbeiten.Es benutzt sun.misc.Unsafe.

Es benutzt Unsafe.addressSize() um die Größe eines nativen Zeigers zu erhalten und Unsafe.arrayIndexScale( Object[].class ) für die Größe einer Java-Referenz.

Es verwendet den Feldoffset 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 );
    }
}

Ohne sich mit Instrumentierung 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, kurz bevor Sie den verwendeten Speicher erhalten. Dadurch senken Sie 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 dividieren, um zu erhalten, wie viel Speicher ein Lauf benötigt.Darüber hinaus können Sie das Ganze mehrmals ausführen und einen Durchschnitt bilden.

Es gibt keinen Methodenaufruf, falls Sie danach fragen.Mit ein wenig Recherche könnten Sie vermutlich Ihre eigene schreiben.Eine bestimmte Instanz hat eine feste Größe, die sich aus der Anzahl der Referenzen und Grundwerte sowie den Buchhaltungsdaten der Instanz ergibt.Sie würden einfach den Objektgraphen ablaufen.Je weniger unterschiedlich die Zeilentypen sind, desto einfacher.

Wenn das zu langsam ist oder einfach mehr Ärger macht, als es wert ist, gibt es immer die gute, altmodische Faustregel für das Zeilenzählen.

Ich habe einmal einen Schnelltest geschrieben, um spontan 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 Heap-Speicherplatz zu messen.Der Schlüssel ist getFreeMemory(), welche Fordert GC-Ausführungen an und wartet darauf, dass sich die gemeldete freie Heap-Größe stabilisiert.Die Ausgabe oben ist:

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

Das ist es, was wir angesichts des Ausrichtungsverhaltens und des möglichen Heap-Block-Header-Overheads erwarten.

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

Verwenden Sie einfach Java Visual VM.

Es verfügt über alles, was Sie zum Profilieren und Debuggen von Speicherproblemen benötigen.

Es verfügt außerdem über eine OQL-Konsole (Object Query Language), mit der Sie viele nützliche Dinge tun können, darunter: sizeof(o)

Meine Antwort basiert auf dem von Nick bereitgestellten Code.Dieser Code misst die Gesamtzahl der Bytes, die vom serialisierten Objekt belegt sind.Dies misst also tatsächlich Serialisierungsmaterial + einfachen Objektspeicherbedarf (z. B. einfach serialisieren). int und Sie werden sehen, dass die Gesamtzahl der serialisierten Bytes nicht stimmt 4).Wenn Sie also möchten, dass die rohe Bytezahl genau für Ihr Objekt verwendet wird, müssen Sie diesen Code ein wenig ändern.Etwa 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 Fälle geben, die nicht abgedeckt sind.


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

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

long heapSizeBefore = Runtime.getRuntime().totalMemory();

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

Die Größe gibt Ihnen den Anstieg der Speichernutzung des JVM aufgrund der Objekterstellung an und ist normalerweise die Größe des Objekts.

Diese Antwort bezieht sich nicht auf die Objektgröße, sondern darauf, ob Sie ein Array verwenden, um die Objekte unterzubringen.wie viel Speichergröße für das Objekt reserviert wird.

Arrays, Listen oder Zuordnungen all dieser Sammlungen werden also keine Objekte wirklich speichern (nur zum Zeitpunkt der Grundelemente ist eine tatsächliche Objektspeichergröße erforderlich), 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 String-Referenz ODER eine doppelte Objektreferenz handeln. Der benötigte Speicher hängt jedoch von der Objekterstellung ab.

Beispiel: Wenn ich ein Objekt für die folgende Klasse erstelle ReferenceMemoryTest dann werden 4 + 4 + 4 = 12 Byte 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 also ein Objekt-/Referenzarray erstellt wird, wird sein gesamter Inhalt mit NULL-Referenzen belegt.Und wir wissen, dass jede Referenz 4 Bytes erfordert.

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

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

Angenommen, ich deklariere eine Klasse mit dem Namen Complex wie:

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

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.

Ich bezweifle, dass Sie dies programmgesteuert tun möchten, es sei denn, Sie möchten es nur einmal ausführen und für die zukünftige Verwendung speichern.Es ist eine kostspielige Sache.In Java gibt es keinen sizeof()-Operator, und selbst wenn es einen gäbe, würde er nur die Kosten der Verweise auf andere Objekte und die Größe der Grundelemente zählen.

Eine Möglichkeit besteht darin, das Ding in eine Datei zu serialisieren und sich die Größe der Datei anzusehen, etwa so:

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

Dies setzt natürlich voraus, dass jedes Objekt eindeutig ist und keine nichtflüchtigen 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 zu addieren (boolean & byte = 1 Byte, short & char = 2 Bytes usw.), um sich in der Mitgliederhierarchie nach unten zu arbeiten.Aber das ist mühsam und teuer und bewirkt am Ende das Gleiche, was die Serialisierungsstrategie bewirken würde.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top