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 OutOfMemoryError
s 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.
Antworten:
Sie können das Paket java.lang.instrument verwenden
Kompilieren Sie diese Klasse und fügen Sie sie in ein JAR ein:
Fügen Sie Folgendes hinzu
MANIFEST.MF
:Verwenden Sie getObjectSize:
Rufen Sie auf mit:
quelle
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.Sie sollten jol verwenden , ein Tool, das im Rahmen des OpenJDK-Projekts entwickelt wurde.
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ückgegebenSie können die geringe Größe einer Objektinstanz mithilfe von
ClassLayout.parseClass(Foo.class).toPrintable()
(optional Übergabe einer Instanz antoPrintable
) 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ürjava.util.regex.Pattern
: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 vonPattern.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.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.)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.
quelle
vmDetails
das jetzt istVM.current().details()
.GraphLayout.parseInstance(instance).toFootprint()
Ich fand es nützlicher, Objektgrößen zu verstehenIch 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.
Ergebnisse:
quelle
ObjectSizeCalculator
nur auf HotSpot VM unterstützt wirdVor 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.
quelle
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:
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.
quelle
Wenn Sie nur wissen möchten, wie viel Speicher in Ihrer JVM verwendet wird und wie viel frei ist, können Sie Folgendes ausprobieren:
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.
quelle
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
quelle
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.
https://github.com/jbellis/jamm
quelle
Sie müssen die Objekte mit Reflexion gehen. Seien Sie vorsichtig wie Sie:
byte
theoretisch 1 Byte ist, heißt das nicht, dass nur eines im Speicher benötigt wird.HashMap
oder 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.
quelle
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:
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.quelle
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)Die
java.lang.instrument.Instrumentation
Klasse bietet eine gute Möglichkeit, die Größe eines Java-Objekts zu ermitteln. Sie müssen jedoch ein definierenpremain
und 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
Unsafe
Klasse von dersun.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 HilfsklasseUtilUnsafe
, um einen Verweis auf dassun.misc.Unsafe
Objekt zu erhalten.quelle
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.
quelle
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:
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.
quelle
System.gc()
Benachrichtigt nicht nur, dass Sie GC möchten? Es kann nicht garantiert werden, dass der GC überhaupt aufgerufen wird.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 undUnsafe.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.
quelle
Instrumentation
weil ich TomcatObjectSizeCalculator
nichtJOL
starte , 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 denAbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()
Refactor-internalSizeOf
Code, um Class und Enum zu ignorierenIch suchte nach einer Laufzeitberechnung einer Objektgröße, die die folgenden Anforderungen erfüllt:
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.
}}
quelle
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.
quelle
Ich habe einmal einen Schnelltest geschrieben, um im Handumdrehen abzuschätzen:
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: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.
quelle
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)
quelle
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":
quelle
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
int
und Sie werden sehen, dass die Gesamtmenge der serialisierten Bytes nicht vorhanden ist4
). 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: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.
quelle
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.
quelle
Größe gibt Ihnen die Zunahme der Speichernutzung des JVM aufgrund der Objekterstellung und das ist in der Regel die Größe des Objekts.
quelle
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
PRIMITIVE
OBJEKTE
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
ReferenceMemoryTest
4 + 4 + 4 = 12 Bytes Speicher erstellt. Der Speicher kann unterschiedlich sein, wenn Sie versuchen, die Referenzen zu initialisieren.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)
quelle
Angenommen, ich deklariere eine Klasse mit dem Namen
Complex
:Um zu sehen, wie viel Speicher Live-Instanzen dieser Klasse zugewiesen ist:
quelle
Für JSONObject kann Ihnen der folgende Code helfen.
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.
quelle
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:
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.
quelle
java.lang.Integer
erzeugt 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 dazunull
benötigt eine serialisierte Referenz ein Byte anstelle der vier oder acht Bytes im Heapspeicher.