230 votes

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

L'espace mémoire consommé par un objet avec 100 attributs est-il le même que celui de 100 objets, avec un attribut chacun ?

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

191voto

VonC Points 414372

Selon JavaWorld

Une simple Object prend 8 octets ;

La réponse est donc "non".

Remarque : En outre, le JDK 1.5 a ajouté la fonction un Instrumentation interface qui comprend [a getObjectSize() méthode .

Mindprod résume les différentes tailles, qui ne changent pas entre les systèmes d'exploitation de 32 et 64 bits, à l'exception de références aux objets . Certaines JVM à 64 bits peuvent compressé leurs références d'objets afin d'éviter les surcharges lorsqu'il est exécuté sur une plate-forme à 32 bits.

Une JVM est libre de stocker les données comme bon lui semble en interne, big ou little endian, avec n'importe quelle quantité de remplissage ou de surcharge, bien que les primitives doivent se comporter comme si elles avaient les tailles officielles.
Par exemple, la JVM ou le compilateur natif peuvent décider de stocker un fichier boolean[] en morceaux de 64 bits comme un BitSet . Il n'est pas obligé de vous le dire, tant que le programme donne les mêmes réponses.

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

Ensuite, bien sûr, le matériel et le système d'exploitation ont des caches multicouches, un cache sur la puce, un cache SRAM, un cache DRAM, un ensemble de travail RAM ordinaire et un stockage de secours 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 de RAM.


Le site Article de JavaWorld donne un peu plus de détails sur la surcharge de stockage en fonction du conteneur utilisé :

Par exemple, les wrappers peuvent également être coûteux par rapport aux types primitifs pour les attributs :

Integer : Le résultat de 16 octets est un peu moins bon que ce à quoi je m'attendais, car une valeur int peut tenir dans seulement 4 octets supplémentaires. L'utilisation d'un Integer me coûte un surcoût de mémoire de 300 % par rapport à la possibilité de stocker la valeur comme un type primitif.

Long : 16 octets également : Il est clair que la taille réelle des objets sur le tas est sujette à un alignement mémoire de bas niveau effectué par une implémentation particulière de la JVM pour un type de CPU particulier. Il semble qu'un Long représente 8 octets de surcharge Object, plus 8 octets supplémentaires pour la valeur longue réelle. En revanche, Integer avait un trou de 4 octets inutilisé, très probablement parce que la JVM que j'utilise force l'alignement des objets sur une frontière de mot de 8 octets.

D'autres conteneurs sont également coûteux :

Tableaux multidimensionnels Il s'agit d'une autre surprise.
Les développeurs utilisent généralement des constructions telles que int[dim1][dim2] dans le domaine du calcul numérique et scientifique.
Dans un int[dim1][dim2] chaque exemple de tableau imbriqué int[dim2] est un tableau Object à part entière. Chacun d'eux ajoute la surcharge habituelle des tableaux de 16 octets. Lorsque je n'ai pas besoin d'un tableau triangulaire ou irrégulier, cela représente une surcharge pure. L'impact augmente lorsque les dimensions du tableau sont très différentes.
Par exemple, un int[128][2] prend 3 600 octets. Comparé aux 1 040 octets d'une instance de int[256] utilise (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 surcharge est de presque 19 ! Comparez cela à la situation en C/C++, où la même syntaxe n'ajoute aucune surcharge de stockage.

String : a String La croissance de la mémoire de l'entreprise suit la croissance de son tableau interne de chars. Cependant, le String ajoute 24 octets supplémentaires de surcharge.
Pour une chaîne de caractères non vide de 10 caractères ou moins, les frais généraux supplémentaires par rapport à la charge utile (2 octets pour chaque caractère plus 4 octets pour la longueur) vont de 100 à 400 %.

0 votes

Int[128][6] : 128 tableaux de 6 ints - 768 ints au total, 3072 octets de données + 2064 octets de surcharge d'objets = 5166 octets au total. int[256] : 256 ints au total - donc non comparable. int[768] : 3072 octets de données + 16 bytes de surcharge - environ 3/5ème de l'espace du tableau 2D - pas tout à fait 246% de surcharge !

0 votes

Ah, l'article original utilisait int[128][2] et non int[128][6] - je me demande comment cela a été modifié. Cela montre aussi que les exemples extrêmes peuvent raconter une histoire différente.

0 votes

@Jeebee : J'ai corrigé les fautes de frappe. int[128][2] est devenu int[128][6] à cause d'un bug dans l'éditeur Javascript : les liens sont référencés avec [aTest][x] et l'éditeur a supposé que [128][2] était une adresse de lien ! Il a "réorganisé" les index de ces liens, changeant le [2] en [6]... délicat !

29voto

Jon Skeet Points 692016

Chaque objet a une certaine surcharge pour son moniteur associé et les informations de type, ainsi que les champs eux-mêmes. Au-delà de cela, les champs peuvent être disposés à peu près comme la JVM l'entend (je crois) - mais comme l'a dit la montré dans une autre réponse au moins un peu de Les JVMs s'emballent de manière assez serrée. Considérez une classe comme celle-ci :

public class SingleByte
{
    private byte b;
}

vs

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

Sur une JVM 32 bits, je m'attends à 100 instances de SingleByte pour prendre 1200 octets (8 octets d'overhead + 4 octets pour le champ dû à l'espacement/alignement). Je m'attends à ce qu'une instance de OneHundredBytes pour prendre 108 octets - l'overhead, et ensuite 100 octets, emballés. Cela peut certainement varier d'une JVM à l'autre - une implémentation peut décider de ne pas empaqueter les champs dans le fichier OneHundredBytes Ce qui fait qu'il prend 408 octets (= 8 octets d'overhead + 4 * 100 octets alignés/padés). Sur une JVM 64 bits, l'overhead pourrait bien être plus important aussi (je n'en suis pas sûr).

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

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

9 votes

En fait, une instance de SingleByte prendrait 16 octets sur une JVM Sun, soit 8 octets d'overhead, 4 octets pour le champ, puis 4 octets pour le padding de l'objet, puisque le compilateur HotSpot arrondit tout à des multiples de 8.

8voto

nazar_art Points 1445

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 concernent la mémoire. L'exemple de codage suivant dé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));
     }
 }

5voto

Mendelt Points 21583

Non, l'enregistrement d'un objet prend aussi un peu de mémoire. 100 objets avec 1 attribut prendront plus de mémoire.

5voto

Nikhil Agrawal Points 3687

La question sera très vaste.

Cela dépend de la variable de classe ou vous pouvez l'appeler "states memory usage" en java.

Il a également besoin d'un peu de mémoire supplémentaire pour les en-têtes et le référencement.

La mémoire du tas utilisée par un objet Java comprend

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

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

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

Les objets en Java requièrent également certaines informations de "gestion interne", telles que l'enregistrement de la classe, de l'ID et des indicateurs d'état d'un objet, par exemple si l'objet est actuellement accessible, s'il est actuellement verrouillé par synchronisation, etc.

La taille de l'en-tête des objets Java varie selon qu'il s'agit de jvm 32 ou 64 bits.

Bien qu'il s'agisse des principaux consommateurs de mémoire, jvm requiert parfois des champs supplémentaires, par exemple pour l'alignement du code, etc.

Tailles des types primitifs

booléen & octet -- 1

char & short -- 2

int & float -- 4

long & double -- 8

0 votes

Les lecteurs pourront également trouver ce document très éclairant : cs.virginia.edu/kim/publicité/pldi09tutorials/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X