Mindprod weist darauf hin, dass dies keine einfache Frage ist:
Eine JVM kann Daten nach Belieben intern, groß oder klein, mit beliebiger Menge an Auffüllung oder Aufwand speichern, obwohl sich Grundelemente so verhalten müssen, als hätten sie die offiziellen Größen.
Beispielsweise kann die JVM oder der native Compiler entscheiden, einen boolean[]
in 64 Bit langen Blöcken wie a zu speichern BitSet
. Es muss Ihnen nicht sagen, solange das Programm die gleichen Antworten gibt.
- Möglicherweise werden einige temporäre Objekte auf dem Stapel zugewiesen.
- Es kann einige Variablen oder Methodenaufrufe optimieren, die vollständig nicht mehr existieren, und sie durch Konstanten ersetzen.
- Es kann Methoden oder Schleifen versionieren, dh zwei Versionen einer Methode kompilieren, die jeweils für eine bestimmte Situation optimiert sind, und dann im Voraus entscheiden, welche aufgerufen werden soll.
Dann haben die Hardware und das Betriebssystem natürlich mehrschichtige Caches, einen Chip-Cache, einen SRAM-Cache, einen DRAM-Cache, einen normalen RAM-Arbeitssatz und einen Sicherungsspeicher auf der Festplatte. Ihre Daten können auf jeder Cache-Ebene dupliziert werden. All diese Komplexität bedeutet, dass Sie den RAM-Verbrauch nur sehr grob vorhersagen können.
Messmethoden
Sie können verwenden Instrumentation.getObjectSize()
, um eine Schätzung des von einem Objekt verbrauchten Speichers zu erhalten.
Um das tatsächliche Objektlayout, den Footprint und die Referenzen zu visualisieren , können Sie das JOL-Tool (Java Object Layout) verwenden .
Objektheader und Objektreferenzen
In einem modernen 64-Bit-JDK verfügt ein Objekt über einen 12-Byte-Header, der mit einem Vielfachen von 8 Byte aufgefüllt ist, sodass die minimale Objektgröße 16 Byte beträgt. Bei 32-Bit-JVMs beträgt der Overhead 8 Byte, aufgefüllt mit einem Vielfachen von 4 Byte. (Von Dmitry Spikhalskiy Antwort , Jayen Antwort und Javaworld .)
In der Regel sind Referenzen 4 Byte auf 32-Bit-Plattformen oder auf 64-Bit-Plattformen bis zu -Xmx32G
; und 8 Bytes über 32 GB ( -Xmx32G
). (Siehe komprimierte Objektreferenzen .)
Infolgedessen benötigt eine 64-Bit-JVM normalerweise 30-50% mehr Heap-Speicherplatz. ( Soll ich eine 32- oder 64-Bit-JVM verwenden ? , 2012, JDK 1.7)
Boxed-Typen, Arrays und Strings
Boxed Wrapper haben im Vergleich zu primitiven Typen (aus JavaWorld ) Overhead :
Integer
: Das 16-Byte-Ergebnis ist etwas schlechter als erwartet, da ein int
Wert in nur 4 zusätzliche Bytes passt. Die Verwendung von a Integer
kostet mich 300 Prozent Speicheraufwand im Vergleich dazu, wenn ich den Wert als primitiven Typ speichern kann
Long
: 16 Bytes auch: Die tatsächliche Objektgröße auf dem Heap unterliegt eindeutig einer Speicherausrichtung auf niedriger Ebene, die von einer bestimmten JVM-Implementierung für einen bestimmten CPU-Typ durchgeführt wird. Es sieht so aus, als ob a Long
8 Byte Objekt-Overhead plus 8 Byte mehr für den tatsächlichen Long-Wert beträgt. Im Gegensatz dazu Integer
hatte ein nicht verwendetes 4-Byte-Loch, höchstwahrscheinlich, weil die JVM, die ich verwende, die Objektausrichtung an einer 8-Byte-Wortgrenze erzwingt.
Andere Container sind ebenfalls teuer:
Mehrdimensionale Arrays : Es bietet eine weitere Überraschung.
Entwickler verwenden üblicherweise Konstrukte wie int[dim1][dim2]
im numerischen und wissenschaftlichen Rechnen.
In einer int[dim1][dim2]
Array-Instanz ist jedes verschachtelte int[dim2]
Array ein Object
eigenständiges Array . Jeder fügt den üblichen 16-Byte-Array-Overhead hinzu. Wenn ich kein dreieckiges oder zerlumptes Array benötige, bedeutet das reinen Overhead. Die Auswirkung nimmt zu, wenn sich die Array-Abmessungen stark unterscheiden.
Eine int[128][2]
Instanz benötigt beispielsweise 3.600 Bytes. Im Vergleich zu den 1.040 Bytes, die eine int[256]
Instanz verwendet (mit derselben Kapazität), bedeuten 3.600 Bytes einen Overhead von 246 Prozent. Im Extremfall byte[256][1]
beträgt der Overhead-Faktor fast 19! Vergleichen Sie dies mit der C / C ++ - Situation, in der dieselbe Syntax keinen Speicheraufwand verursacht.
String
: String
Das Speicherwachstum von a verfolgt das Wachstum seines internen Char-Arrays. Die String
Klasse fügt jedoch weitere 24 Byte Overhead hinzu.
Bei einer Nicht-Leere mit String
einer Größe von 10 Zeichen oder weniger liegen die zusätzlichen Gemeinkosten im Verhältnis zur Nutzlast (2 Byte für jedes Zeichen plus 4 Byte für die Länge) zwischen 100 und 400 Prozent.
Ausrichtung
Betrachten Sie dieses Beispielobjekt :
class X { // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}
Eine naive Summe würde bedeuten, dass eine Instanz von X
17 Bytes verwenden würde. Aufgrund der Ausrichtung (auch als Auffüllen bezeichnet) weist die JVM den Speicher jedoch in Vielfachen von 8 Bytes zu, sodass anstelle von 17 Bytes 24 Bytes zugewiesen werden.
Das hängt von der Architektur / jdk ab. Bei einer modernen JDK- und 64-Bit-Architektur verfügt ein Objekt über einen 12-Byte-Header und einen Abstand von 8 Byte. Die minimale Objektgröße beträgt also 16 Byte. Sie können ein Tool namens Java Object Layout verwenden , um eine Größe zu bestimmen und Details zum Objektlayout und zur internen Struktur einer Entität abzurufen oder diese Informationen anhand einer Klassenreferenz zu erraten. Beispiel für eine Ausgabe für Integer in meiner Umgebung:
Für Integer beträgt die Instanzgröße also 16 Byte, da 4 Byte int direkt nach dem Header und vor dem Auffüllen der Grenze komprimiert werden.
Codebeispiel:
Wenn Sie Maven verwenden, um JOL zu erhalten:
quelle
Jedes Objekt hat einen bestimmten Overhead für die zugehörigen Monitor- und Typinformationen sowie für die Felder selbst. Darüber hinaus können Felder so ziemlich angelegt werden, wie es die JVM für richtig hält (glaube ich) - aber wie in einer anderen Antwort gezeigt , werden zumindest einige JVMs ziemlich dicht gepackt sein. Stellen Sie sich eine Klasse wie diese vor:
vs.
Bei einer 32-Bit-JVM würde ich erwarten, dass 100 Instanzen
SingleByte
1200 Byte benötigen (8 Byte Overhead + 4 Byte für das Feld aufgrund von Auffüllen / Ausrichtung). Ich würde erwarten, dass eine InstanzOneHundredBytes
108 Bytes benötigt - der Overhead und dann 100 Bytes, gepackt. Es kann jedoch je nach JVM variieren - eine Implementierung kann entscheiden, die Felder nicht einzupackenOneHundredBytes
, was dazu führt, dass 408 Bytes benötigt werden (= 8 Bytes Overhead + 4 * 100 ausgerichtete / aufgefüllte Bytes). Bei einer 64-Bit-JVM ist der Overhead möglicherweise auch größer (nicht sicher).BEARBEITEN: Siehe den Kommentar unten; Anscheinend werden HotSpot-Pads an 8-Byte-Grenzen anstatt an 32 angehängt, sodass jede Instanz von
SingleByte
16 Bytes benötigt.In jedem Fall ist das "einzelne große Objekt" mindestens so effizient wie mehrere kleine Objekte - für einfache Fälle wie diesen.
quelle
Der insgesamt genutzte / freie Speicher eines Programms kann im Programm über abgerufen werden
Die Laufzeit hat mehrere Methoden, die sich auf den Speicher beziehen. Das folgende Codierungsbeispiel zeigt die Verwendung.
quelle
Es scheint, dass jedes Objekt auf 32-Bit-Systemen einen Overhead von 16 Byte hat (und auf 64-Bit-Systemen 24 Byte).
http://algs4.cs.princeton.edu/14analysis/ ist eine gute Informationsquelle. Ein Beispiel unter vielen guten ist das folgende.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf ist auch sehr informativ, zum Beispiel:
quelle
Nein.
quelle
Nein, das Registrieren eines Objekts benötigt ebenfalls etwas Speicher. 100 Objekte mit 1 Attribut belegen mehr Speicher.
quelle
Die Frage wird sehr weit gefasst sein.
Dies hängt von der Klassenvariablen ab, oder Sie können die Speichernutzung in Java als Status aufrufen.
Es hat auch einige zusätzliche Speicheranforderungen für Header und Referenzierungen.
Der von einem Java-Objekt verwendete Heap-Speicher enthält
Speicher für primitive Felder entsprechend ihrer Größe (siehe unten für Größen primitiver Typen);
Speicher für Referenzfelder (jeweils 4 Bytes);
einen Objektheader, der aus einigen Bytes "Housekeeping" -Informationen besteht;
Objekte in Java erfordern auch einige "Housekeeping" -Informationen, z. B. das Aufzeichnen der Klassen-, ID- und Statusflags eines Objekts, z. B. ob das Objekt derzeit erreichbar ist, derzeit synchronisiert ist usw.
Die Größe des Java-Objekt-Headers variiert je nach 32- und 64-Bit-JVM.
Obwohl dies die Hauptspeicherkonsumenten sind, benötigt jvm manchmal auch zusätzliche Felder, wie zum Beispiel für die Ausrichtung des Codes usw.
Größen primitiver Typen
Boolescher Wert & Byte - 1
char & short - 2
int & float - 4
lang & doppelt - 8
quelle
Ich habe sehr gute Ergebnisse mit dem in einer anderen Antwort erwähnten Ansatz java.lang.instrument.Instrumentation erzielt . Gute Beispiele für seine Verwendung finden Sie im Eintrag Instrumentation Memory Counter aus dem JavaSpecialists 'Newsletter und in der Bibliothek java.sizeOf auf SourceForge.
quelle
Falls es für jemanden nützlich ist, können Sie einen kleinen Java-Agenten von meiner Website herunterladen , um die Speichernutzung eines Objekts abzufragen . Damit können Sie auch die "tiefe" Speichernutzung abfragen.
quelle
(String, Integer)
Guava-Cache pro Element zu erhalten. Vielen Dank!Nein, 100 kleine Objekte benötigen mehr Informationen (Speicher) als ein großes.
quelle
Die Regeln für den Speicherbedarf hängen von der JVM-Implementierung und der CPU-Architektur ab (z. B. 32 Bit gegenüber 64 Bit).
Die detaillierten Regeln für die SUN JVM finden Sie in meinem alten Blog
Grüße, Markus
quelle