it-swarm.com.de

Was ist der Speicherverbrauch eines Objekts in Java?

Verbraucht ein Objekt mit 100 Attributen denselben Speicherplatz wie 100 Objekte mit jeweils einem Attribut?

Wie viel Speicher ist für ein Objekt reserviert?
Wie viel zusätzlicher Speicherplatz wird beim Hinzufügen eines Attributs verwendet?

202
manuel

Mindprod weist darauf hin, dass dies keine einfache Frage ist:

Es steht einer JVM frei, Daten in beliebiger interner, Big- oder Little-Endian-Form mit beliebigem Abstand oder Overhead zu speichern, obwohl sich die Grundelemente so verhalten müssen, als hätten sie die offiziellen Größen.
Zum Beispiel könnte die JVM oder der native Compiler entscheiden, einen boolean[] In 64-Bit langen Blöcken wie einem BitSet zu speichern. Es muss Ihnen nicht sagen, solange das Programm die gleichen Antworten gibt.

  • Möglicherweise werden einige temporäre Objekte auf dem Stapel zugewiesen.
  • Möglicherweise werden einige Variablen oder Methodenaufrufe optimiert, die nicht mehr existieren, und durch Konstanten ersetzt.
  • Sie kann Methoden oder Schleifen versionieren, d. H. 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 Multilayer-Caches, einen Chip-Cache, einen SRAM-Cache, einen DRAM-Cache, einen gewöhnlichen 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 Instrumentation.getObjectSize() verwenden, um eine Schätzung des von einem Objekt belegten 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.

Objektköpfe und Objektreferenzen

In einem modernen 64-Bit-JDK hat ein Objekt einen 12-Byte-Header, der auf ein Vielfaches 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 auf ein Vielfaches von 4 Byte. (Von Dmitry Spikhalskiys Antwort , Jayens Antwort und JavaWorld .)

Normalerweise 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 würde eine 64-Bit-JVM in der Regel 30-50% mehr Heapspeicherplatz benötigen. ( Soll ich eine 32- oder 64-Bit-JVM verwenden? , 2012, JDK 1.7)

Boxed Types, Arrays und Strings

Boxed Wrapper haben Overhead im Vergleich zu primitiven Typen (von JavaWorld ):

  • Integer: Das 16-Byte-Ergebnis ist etwas schlechter als erwartet, da ein int -Wert in nur passen kann 4 zusätzliche Bytes. Die Verwendung eines Integer kostet mich 300 Prozent des Speicheraufwands im Vergleich zu dem Zeitpunkt, zu dem ich den Wert als primitiven Typ speichern kann

  • Long: 16 Bytes auch: Die tatsächliche Objektgröße auf dem Heap hängt eindeutig von der Ausrichtung des Speichers auf niedriger Ebene ab, die von einer bestimmten JVM durchgeführt wird Implementierung für einen bestimmten CPU-Typ. Es sieht so aus, als ob Long 8 Bytes des Objekt-Overheads plus 8 Bytes mehr für den tatsächlichen langen Wert beträgt. Im Gegensatz dazu hatte Integer eine ungenutzte 4-Byte-Lücke, höchstwahrscheinlich, weil die von mir verwendete JVM die Objektausrichtung an einer 8-Byte-Wortgrenze erzwingt.

Andere Behälter sind auch teuer:

  • Mehrdimensionale Arrays : Es bietet eine weitere Überraschung.
    Entwickler verwenden üblicherweise Konstrukte wie int[dim1][dim2] Im numerischen und wissenschaftlichen Rechnen.

    In einer Array-Instanz int[dim1][dim2] Ist jedes verschachtelte Array int[dim2] Ein Object für sich. 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 wächst, wenn sich die Array-Abmessungen stark unterscheiden.

    Beispielsweise benötigt eine Instanz int[128][2] 3.600 Byte. Im Vergleich zu den 1.040 Bytes, die eine int[256] - Instanz verwendet (die dieselbe Kapazität hat), bedeuten 3.600 Bytes einen Overhead von 246 Prozent. Im Extremfall von byte[256][1] Beträgt der Overheadfaktor fast 19! Vergleichen Sie dies mit der C/C++ - Situation, in der die gleiche Syntax keinen zusätzlichen Speicheraufwand verursacht.

  • String: Das Speicherwachstum eines String verfolgt das Wachstum seines internen Char-Arrays. Die Klasse String fügt jedoch weitere 24 Byte Overhead hinzu.

    Bei einem nicht leeren String mit einer Größe von 10 Zeichen oder weniger liegen die zusätzlichen Zusatzkosten 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 benötigt. Aufgrund der Ausrichtung (auch als Auffüllen bezeichnet) weist die JVM den Speicher jedoch in Vielfachen von 8 Bytes zu, sodass statt 17 Bytes 24 Bytes zugewiesen werden.

169
VonC

Es kommt auf architektur/jdk an. In einer modernen JDK- und 64-Bit-Architektur hat ein Objekt einen 12-Byte-Header und eine Auffüllung von 8 Byte. Die minimale Objektgröße beträgt daher 16 Byte. Sie können ein Tool namens Java Object Layout verwenden, um eine Größe zu bestimmen und Details über das Objektlayout und die interne Struktur einer Entität abzurufen oder diese Informationen anhand von Klassenreferenzen zu erraten. Beispiel einer Ausgabe für Integer in meiner Umgebung:

Running 64-bit HotSpot VM.
Using compressed oop with 3-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]

Java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

Für Integer beträgt die Instanzgröße 16 Byte, da 4 Byte int direkt nach dem Header und vor dem Auffüllen der Begrenzung komprimiert werden.

Codebeispiel:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

Wenn Sie Maven verwenden, um JOL zu erhalten:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>
30

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 so angeordnet werden, wie es die JVM für richtig hält (ich glaube) - aber als in einer anderen Antwort gezeigt , zumindest einige JVMs werden ziemlich eng gepackt. Betrachten Sie eine Klasse wie diese:

public class SingleByte
{
    private byte b;
}

vs

public class OneHundredBytes
{
    private byte b00, b01, ..., b99;
}

Bei einer 32-Bit-JVM würde ich davon ausgehen, dass 100 Instanzen von SingleByte 1200 Bytes benötigen (8 Bytes Overhead + 4 Bytes für das Feld aufgrund von Auffüllung/Ausrichtung). Ich würde erwarten, dass eine Instanz von OneHundredBytes 108 Bytes benötigt - den Overhead und dann 100 Bytes, gepackt. Dies kann jedoch von JVM zu JVM unterschiedlich sein - eine Implementierung kann entscheiden, die Felder nicht in OneHundredBytes zu packen, was dazu führt, dass 408 Bytes (= 8 Bytes Overhead + 4 * 100 ausgerichtete/aufgefüllte Bytes) benötigt werden. Bei einer 64-Bit-JVM kann der Overhead auch größer sein (nicht sicher).

BEARBEITEN: Siehe den Kommentar unten; Anscheinend füllt HotSpot 8 Bytes anstatt 32 Bytes auf, sodass jede Instanz von SingleByte 16 Bytes benötigt.

In beiden Fällen ist das "einzelne große Objekt" mindestens so effizient wie mehrere kleine Objekte - für einfache Fälle wie diesen.

27
Jon Skeet

Verbraucht ein Objekt mit 100 Attributen denselben Speicherplatz wie 100 Objekte mit jeweils einem Attribut?

Nein.

Wie viel Speicher ist für ein Objekt reserviert?

  • Der Overhead beträgt 8 Byte bei 32 Bit, 12 Byte bei 64 Bit. und dann auf ein Vielfaches von 4 Byte (32 Bit) oder 8 Byte (64 Bit) aufgerundet.

Wie viel zusätzlicher Speicherplatz wird beim Hinzufügen eines Attributs verwendet?

  • Die Attribute reichen von 1 Byte (Byte) bis 8 Byte (Long/Double), aber Referenzen sind entweder 4 Byte oder 8 Byte, je nachdem, ob es sich um 32 Bit handelt oder nicht oder 64-Bit, aber eher, ob -Xmx <32 Gb oder> = 32 Gb ist: Typische 64-Bit-JVMs haben eine Optimierung namens "-UseCompressedOops", die Verweise auf 4 Bytes komprimiert, wenn der Heap unter 32 Gb liegt.
5
Jayen

Nein, das Registrieren eines Objekts beansprucht auch etwas Speicherplatz. 100 Objekte mit 1 Attribut belegen mehr Speicher.

5
Mendelt

Es scheint, dass jedes Objekt einen Overhead von 16 Byte auf 32-Bit-Systemen (und 24 Byte auf 64-Bit-Systemen) hat.

http://algs4.cs.princeton.edu/14analysis/ ist eine gute Informationsquelle. Ein Beispiel unter vielen guten ist das folgende.

enter image description here

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-Java-tutorial.pdf ist auch sehr informativ, zum Beispiel:

enter image description here

5
Arun

Der insgesamt genutzte/freie Speicher eines Programms kann im Programm über abgerufen werden

Java.lang.Runtime.getRuntime();

Die Laufzeit hat mehrere Methoden, die sich auf den Speicher beziehen. Das folgende Codebeispiel zeigt die Verwendung.

package test;

 import Java.util.ArrayList;
 import Java.util.List;

 public class PerformanceTest {
     private static final long MEGABYTE = 1024L * 1024L;

     public static long bytesToMegabytes(long bytes) {
         return bytes / MEGABYTE;
     }

     public static void main(String[] args) {
         // I assume you will know how to create a object Person yourself...
         List < Person > list = new ArrayList < Person > ();
         for (int i = 0; i <= 100000; i++) {
             list.add(new Person("Jim", "Knopf"));
         }
         // Get the Java runtime
         Runtime runtime = Runtime.getRuntime();
         // Run the garbage collector
         runtime.gc();
         // Calculate the used memory
         long memory = runtime.totalMemory() - runtime.freeMemory();
         System.out.println("Used memory is bytes: " + memory);
         System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
     }
 }
4
nazar_art

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 Verweise.

Der von einem Java -Objekt verwendete Heapspeicher enthält

  • speicher für primitive Felder entsprechend ihrer Größe (siehe unten für Größen von primitiven Typen);

  • speicher für Referenzfelder (je 4 Byte);

  • einen Objektkopf, der aus einigen Bytes "Housekeeping" -Information besteht;

Objekte in Java erfordert auch einige "Housekeeping" -Informationen, z. B. das Aufzeichnen der Klassen-, ID- und Statusflags eines Objekts, z.

Die Größe des Java-Objektheaders variiert zwischen 32- und 64-Bit-JVM.

Obwohl dies die Hauptspeicherkonsumenten sind, benötigt jvm auch manchmal zusätzliche Felder, wie zum Ausrichten des Codes e.t.c.

Größen primitiver Typen

Boolean & Byte - 1

char & short - 2

int & float - 4

long & double - 8

3
Nikhil Agrawal

Ich habe mit dem in einer anderen Antwort erwähnten Ansatz Java.lang.instrument.Instrumentation sehr gute Ergebnisse erzielt. Gute Anwendungsbeispiele finden Sie im Eintrag Instrumentation Memory Counter im JavaSpecialists 'Newsletter und im Java.sizeOf Bibliothek in SourceForge.

3
Matt Passell

Falls es für jemanden nützlich ist, können Sie von meiner Website ein kleines Java-Agent zum Abfragen der Speichernutzung eines Objekts herunterladen. Damit können Sie auch die "tiefe" Speichernutzung abfragen.

2
Neil Coffey

nein, 100 kleine Objekte benötigen mehr Informationen (Speicher) als ein großes.

1
Burkhard

Die Regeln für den Speicherverbrauch 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 unter mein alter Blog

Grüße, Markus

0
kohlerm