web-dev-qa-db-fra.com

Quelle est la consommation de mémoire d'un objet en Java?

L'espace mémoire utilisé par un objet comportant 100 attributs est-il identique à celui de 100 objets, avec un attribut chacun?

Combien de mémoire est allouée à un objet?
Combien d’espace supplémentaire est utilisé lors de l’ajout d’un attribut?

202
manuel

Mindprod souligne qu'il n'est pas simple de répondre à cette question:

Une machine virtuelle Java est libre de stocker des données comme bon lui semble en interne, qu’elles soient grandes ou petites, avec n'importe quel remplissage ou surcharge, bien que les primitives doivent se comporter comme si elles avaient les tailles officielles.
Par exemple, la machine virtuelle Java ou le compilateur natif peut décider de stocker un boolean[] Dans des fragments de 64 bits comme un BitSet. Il n'est pas nécessaire de vous le dire, tant que le programme donne les mêmes réponses.

  • Cela pourrait allouer des objets temporaires sur la pile.
  • Cela peut optimiser certaines variables ou des appels de méthode totalement hors d’existence en les remplaçant par des constantes.
  • Il peut s’agir de versions de méthodes ou de boucles, c’est-à-dire de compiler deux versions d’une méthode, chacune optimisée pour une situation donnée, puis de décider à l’avance laquelle appeler.

Ensuite, bien sûr, le matériel et le système d'exploitation ont des caches multicouches, sur cache-puce, cache SRAM, cache DRAM, RAM ensemble de travail et sauvegarde sur disque. Vos données peuvent être dupliquées à chaque niveau de cache Toute cette complexité signifie que vous ne pouvez prédire que très approximativement la consommation RAM.

Méthodes de mesure

Vous pouvez utiliser Instrumentation.getObjectSize() pour obtenir une estimation de la mémoire utilisée par un objet.

Pour visualiser la présentation, l'encombrement et les références de l'objet actual, vous pouvez utiliser l'outil JOL (Java Object Layout) .

En-têtes d'objet et références d'objet

Dans un JDK 64 bits moderne, un objet possède un en-tête de 12 octets, complété par un multiple de 8 octets, de sorte que la taille minimale de l'objet est de 16 octets. Pour les machines virtuelles Java 32 bits, la surcharge est de 8 octets, complétée avec un multiple de 4 octets. (De réponse de Dmitry Spikhalskiy , réponse de Jayen , et JavaWorld .)

En règle générale, les références sont 4 octets sur des plates-formes 32 bits ou 64 bits jusqu'à -Xmx32G; et 8 octets supérieurs à 32 Go (-Xmx32G). (Voir références d'objet compressé .)

En conséquence, une machine virtuelle Java 64 bits nécessiterait généralement 30 à 50% d'espace supplémentaire. ( Dois-je utiliser une JVM 32 ou 64 bits? , 2012, JDK 1.7)

Types, tableaux et chaînes en boîte

Les wrappers en boîte ont une surcharge comparée aux types primitifs (de JavaWorld ):

  • Integer: le résultat sur 16 octets est un peu pire que prévu, car une valeur de int peut entrer dans 4 octets supplémentaires. L'utilisation d'un Integer me coûte 300% de mémoire en plus par rapport au moment où je peux stocker la valeur en tant que type primitif

  • Long: 16 octets également: Clairement, la taille réelle de l'objet sur le tas est soumise à un alignement mémoire de bas niveau effectué par une machine virtuelle Java particulière. mise en œuvre pour un type de CPU particulier. Il semble qu'un Long représente 8 octets de surcharge Object, plus 8 octets de plus pour la valeur longue réelle. En revanche, Integer avait un trou inutilisé de 4 octets, probablement parce que la machine virtuelle Java que j'utilise force l'alignement d'objet forcé sur une limite de mot de 8 octets.

Les autres conteneurs sont également coûteux:

  • Tableaux multidimensionnels : cela offre une autre surprise.
    Les développeurs utilisent couramment des constructions telles que int[dim1][dim2] En calcul numérique et scientifique.

    Dans une instance de tableau int[dim1][dim2], Chaque tableau int[dim2] Imbriqué est un Object à part entière. Chacun ajoute la surcharge de tableau habituelle de 16 octets. Lorsque je n'ai pas besoin d'un tableau triangulaire ou irrégulier, cela représente une simple surcharge. L'impact augmente lorsque les dimensions du tableau sont très différentes.

    Par exemple, une instance int[128][2] Prend 3 600 octets. Par rapport aux 1 040 octets utilisés par une instance int[256] (Qui a la même capacité), 3 600 octets représentent une surcharge de 246%. Dans le cas extrême de byte[256][1], Le facteur de frais généraux est presque 19! Comparez cela à la situation C/C++ dans laquelle la même syntaxe n'ajoute aucune surcharge de stockage.

  • String: la croissance de la mémoire de String suit la croissance de son tableau de caractères interne. Cependant, la classe String ajoute 24 octets supplémentaires de surcharge.

    Pour un String non vide de taille inférieure ou égale à 10 caractères, le surcoût ajouté par rapport à la charge utile utile (2 octets pour chaque caractère plus 4 octets pour la longueur) varie de 100 à 400%.

Alignement

Considérez ceci exemple d'objet :

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
}

Une somme naïve suggérerait qu'une instance de X utilise 17 octets. Toutefois, en raison de l'alignement (également appelé remplissage), la JVM alloue la mémoire en multiples de 8 octets. Par conséquent, au lieu de 17 octets, elle attribuerait 24 octets.

169
VonC

Cela dépend de l'architecture/jdk. Pour une architecture JDK moderne et 64 bits, un objet possède un en-tête de 12 octets et un remplissage de 8 octets. La taille minimale de l'objet est donc de 16 octets. Vous pouvez utiliser un outil appelé Java Object Layout pour déterminer une taille et obtenir des détails sur la disposition des objets et la structure interne de toute entité ou deviner ces informations par référence à une classe. Exemple de sortie pour Integer sur mon environnement:

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

Ainsi, pour Integer, la taille de l'instance est de 16 octets, car 4 octets int sont compactés sur place juste après l'en-tête et avant la limite de remplissage.

Exemple de code:

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

Si vous utilisez maven, pour obtenir JOL:

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

Chaque objet a une surcharge spécifique pour son moniteur associé et les informations de type, ainsi que pour les champs eux-mêmes. Au-delà de cela, les champs peuvent être disposés à peu près de toute façon, mais la JVM juge bon (je crois) - mais comme montré dans une autre réponse , au moins un peu Les machines virtuelles Java seront assez compactes. Considérons un cours comme celui-ci:

public class SingleByte
{
    private byte b;
}

contre

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

Sur une machine virtuelle Java 32 bits, je m'attendrais à ce que 100 instances de SingleByte prennent 1200 octets (8 octets de surcharge + 4 octets pour le champ en raison d'un bourrage/alignement). Je m'attendrais à ce qu'une instance de OneHundredBytes prenne 108 octets - le temps système, puis 100 octets, compressés. Cela peut certainement varier d’une machine virtuelle à l’autre - une implémentation peut décider de ne pas compresser les champs dans OneHundredBytes, ce qui entraîne 408 octets (= 8 octets de surcharge + 4 * 100 octets alignés/remplis). Sur une machine virtuelle Java 64 bits, les frais généraux peuvent également être plus importants (pas sûr).

EDIT: Voir le commentaire ci-dessous; apparemment, HotSpot compresse à 8 octets au lieu de 32, donc chaque instance de SingleByte prendrait 16 octets.

Dans les deux cas, le "seul grand objet" sera au moins aussi efficace que plusieurs petits objets - pour des cas simples comme celui-ci.

27
Jon Skeet

L'espace mémoire utilisé par un objet comportant 100 attributs est-il identique à celui de 100 objets, avec un attribut chacun?

Non.

Combien de mémoire est allouée à un objet?

  • Le temps système est de 8 octets sur 32 bits et de 12 octets sur 64 bits. puis arrondi à un multiple de 4 octets (32 bits) ou 8 octets (64 bits).

Combien d'espace supplémentaire est utilisé lors de l'ajout d'un attribut?

  • Les attributs vont de 1 octet (octet) à 8 octets (long/double), mais les références sont soit 4 octets soit 8 octets selon pas selon qu'il s'agit de 32 bits ou 64 bits, mais plutôt si -Xmx est <32 Go. ou> = 32 Gbits: les machines virtuelles Java 64 bits classiques ont une optimisation appelée "-UseCompressedOops" qui compresse les références à 4 octets si le tas est inférieur à 32 Gb.
5
Jayen

Non, l'enregistrement d'un objet nécessite également un peu de mémoire. 100 objets avec 1 attribut occuperont plus de mémoire.

5
Mendelt

Il semble que chaque objet présente une surcharge de 16 octets sur les systèmes 32 bits (et de 24 octets sur les systèmes 64 bits).

http://algs4.cs.princeton.edu/14analysis/ est une bonne source d'informations. Un exemple parmi beaucoup de bons est le suivant.

enter image description here

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-Java-tutorial.pdf est également très instructif, par exemple:

enter image description here

5
Arun

La mémoire totale utilisée/libre d’un programme peut être obtenue dans le programme via

Java.lang.Runtime.getRuntime();

Le runtime a plusieurs méthodes qui se rapportent à la mémoire. L'exemple de codage suivant montre son utilisation.

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

La question sera très large.

Cela dépend de la variable de classe ou vous pouvez appeler comme utilisation de la mémoire en Java.

Il a également besoin de mémoire supplémentaire pour les en-têtes et les références.

La mémoire de tas utilisée par un Java comprend)

  • mémoire pour les champs primitifs, en fonction de leur taille (voir ci-dessous pour les tailles de types primitifs);

  • mémoire pour les champs de référence (4 octets chacun);

  • un en-tête d'objet, constitué de quelques octets d'informations "d'entretien";

Les objets dans Java nécessitent également certaines informations de "gestion", telles que l'enregistrement de la classe, des ID et des indicateurs d'état d'un objet, par exemple si l'objet est actuellement accessible, la synchronisation est verrouillée, etc.

La taille de l'en-tête d'un objet Java varie entre jvm 32 et 64 bits.

Bien que ce soient les principaux consommateurs de mémoire, jvm requiert également des champs supplémentaires, comme par exemple pour l’alignement du code e.t.c.

tailles des types primitifs

boolean & byte - 1

char & short - 2

int & float - 4

long & double - 8

3
Nikhil Agrawal

J'ai obtenu de très bons résultats avec l'approche Java.lang.instrument.Instrumentation mentionnée dans une autre réponse. Pour de bons exemples d'utilisation, voir l'entrée, compteur de mémoire d'instrumentation de la lettre d'information de JavaSpecialists et du Java.sizeOf bibliothèque sur SourceForge.

3
Matt Passell

Si cela est utile à quiconque, vous pouvez télécharger sur mon site Web un petit agent Java permettant d’interroger l’utilisation de la mémoire d’un objet . Cela vous permettra également d'interroger l'utilisation de la mémoire "profonde".

2
Neil Coffey

non, 100 petits objets ont besoin de plus d'informations (mémoire) que d'un seul gros.

1
Burkhard

Les règles relatives à la quantité de mémoire utilisée dépendent de l'implémentation de la JVM et de l'architecture de la CPU (32 bits par rapport à 64 bits par exemple).

Pour les règles détaillées de la vérification de la JVM Sun mon ancien blog

Cordialement, Markus

0
kohlerm