Réponses:
Mindprod souligne que ce n'est pas une question simple à répondre:
Une machine virtuelle Java est libre de stocker des données comme bon lui semble en interne, grand ou petit 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 peut décider de stocker unboolean[]
morceau de 64 bits comme unBitSet
. Il n'a pas à 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éthode totalement inexistants 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 certaine situation, 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 le cache de puce, le cache SRAM, le cache DRAM, un ensemble de travail RAM ordinaire et un magasin de sauvegarde sur disque. Vos données peuvent être dupliquées à chaque niveau de cache. Toute cette complexité signifie que vous ne pouvez que prédire très approximativement la consommation de RAM.
Vous pouvez utiliser Instrumentation.getObjectSize()
pour obtenir une estimation du stockage consommé par un objet.
Pour visualiser la disposition, l'empreinte et les références d'objets réels , vous pouvez utiliser l' outil JOL (Java Object Layout) .
Dans un JDK 64 bits moderne, un objet a un en-tête de 12 octets, complété à un multiple de 8 octets, donc 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 par un multiple de 4 octets. (De la réponse de Dmitry Spikhalskiy , la réponse de Jayen , et JavaWorld .)
En règle générale, les références sont de 4 octets sur les plates-formes 32 bits ou sur les plates-formes 64 bits jusqu'à -Xmx32G
; et 8 octets au-dessus de 32 Go ( -Xmx32G
). (Voir les références aux objets compressés .)
Par conséquent, une machine virtuelle Java 64 bits nécessiterait généralement 30 à 50% d'espace de segment de mémoire en plus. ( Dois-je utiliser une JVM 32 ou 64 bits?, 2012, JDK 1.7)
Les emballages en boîte ont des frais généraux par rapport aux types primitifs (de JavaWorld ):
Integer
: Le résultat de 16 octets est un peu pire que ce à quoi je m'attendais car uneint
valeur peut tenir dans seulement 4 octets supplémentaires. L'utilisation d'un anInteger
me coûte 300% de mémoire supplémentaire par rapport au moment où je peux stocker la valeur en tant que type primitif
Long
: 16 octets également: De toute évidence, la taille réelle de l'objet sur le tas est soumise à un alignement de mémoire de bas niveau effectué par une implémentation JVM particulière pour un type de processeur particulier. Il ressemble àLong
8 octets de surcharge d'objet, plus 8 octets de plus pour la valeur longue réelle. En revanche,Integer
avait un trou de 4 octets inutilisé, probablement parce que la JVM que j'utilise force l'alignement des objets sur une limite de mot de 8 octets.
D'autres conteneurs sont également coûteux:
Tableaux multidimensionnels : il offre une autre surprise.
Les développeurs utilisent généralement des constructions commeint[dim1][dim2]
dans le calcul numérique et scientifique.Dans une
int[dim1][dim2]
instance de tableau, chaqueint[dim2]
tableau imbriqué est unObject
à part entière. Chacun ajoute la surcharge de tableau 16 octets habituelle. Lorsque je n'ai pas besoin d'un tableau triangulaire ou irrégulier, cela représente un surdébit pur. L'impact augmente lorsque les dimensions de la matrice diffèrent considérablement.Par exemple, une
int[128][2]
instance prend 3 600 octets. Par rapport aux 1 040 octets qu'uneint[256]
instance utilise (qui a la même capacité), 3 600 octets représentent une surcharge de 246%. Dans le cas extrême debyte[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
:String
la croissance de la mémoire de a suit la croissance de sa matrice de caractères interne. Cependant, leString
classe ajoute 24 octets supplémentaires de surcharge.Pour une
String
taille non vide de 10 caractères ou moins, les frais généraux ajoutés par rapport à la charge utile (2 octets pour chaque caractère plus 4 octets pour la longueur), varient de 100 à 400%.
Considérez cet 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
utiliserait 17 octets. Cependant, en raison de l'alignement (également appelé remplissage), la JVM alloue la mémoire par multiples de 8 octets, donc au lieu de 17 octets, elle allouerait 24 octets.
Cela dépend de l'architecture / jdk. Pour une architecture JDK et 64 bits moderne, un objet a 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 de 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 compactés en place juste après l'en-tête et avant la limite de remplissage.
Échantillon 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>
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 mais la JVM le juge opportun (je crois) - mais comme indiqué dans une autre réponse , au moins certaines JVM seront assez compactes. Considérez une classe comme celle-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 SingleByte
prennent 1200 octets (8 octets de surcharge + 4 octets pour le champ en raison du remplissage / alignement). Je m'attends à ce qu'une instance de OneHundredBytes
prenne 108 octets - la surcharge, puis 100 octets, compressés. Cela peut certainement varier selon la JVM - une implémentation peut décider de ne pas emballer les champs dansOneHundredBytes
, ce qui lui fait prendre 408 octets (= 8 octets de surcharge + 4 * 100 octets alignés / remplis). Sur une machine virtuelle Java 64 bits, la surcharge peut également être plus importante (pas sûr).
EDIT: Voir le commentaire ci-dessous; apparemment, HotSpot remplit les limites de 8 octets au lieu de 32, de sorte que chaque instance de SingleByte
prendrait 16 octets.
Quoi qu'il en soit, le "grand objet unique" sera au moins aussi efficace que plusieurs petits objets - pour des cas simples comme celui-ci.
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 liées à 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));
}
}
Il semble que chaque objet a une surcharge de 16 octets sur les systèmes 32 bits (et 24 octets sur les systèmes 64 bits).
http://algs4.cs.princeton.edu/14analysis/ est une bonne source d'information. Un exemple parmi tant d'autres est le suivant.
http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf est également très informatif, par exemple:
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?
Non.
Quelle quantité de mémoire est allouée à un objet?
Combien d'espace supplémentaire est utilisé lors de l'ajout d'un attribut?
La question sera très large.
Cela dépend de la variable de classe ou vous pouvez appeler comme état l'utilisation de la mémoire en java.
Il a également une certaine mémoire supplémentaire pour les en-têtes et le référencement.
La mémoire de tas utilisée par un objet Java comprend
mémoire pour les champs primitifs, selon leur taille (voir ci-dessous pour les tailles des types primitifs);
mémoire pour les champs de référence (4 octets chacun);
un en-tête d'objet, composé de quelques octets d'informations "domestiques";
Les objets en java nécessitent également des informations de "gestion", 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, actuellement verrouillé par synchronisation, etc.
La taille de l'en-tête d'objet Java varie sur jvm 32 et 64 bits.
Bien que ce soient les principaux consommateurs de mémoire, jvm nécessite également des champs supplémentaires, parfois comme pour l'alignement du code, etc.
Tailles des types primitifs
booléen et octet - 1
char & short - 2
int & float - 4
long et double - 8
J'ai obtenu de très bons résultats de l' approche java.lang.instrument.Instrumentation mentionnée dans une autre réponse. Pour de bons exemples de son utilisation, voir l'entrée, Instrumentation Memory Counter de la JavaSpecialists 'Newsletter et la bibliothèque java.sizeOf sur SourceForge.
Au cas où cela serait utile à n'importe qui, vous pouvez télécharger sur mon site Web un petit agent Java pour interroger l'utilisation de la mémoire d'un objet . Il vous permettra également d'interroger l'utilisation de la mémoire "profonde".
(String, Integer)
utilisée par un cache de goyave, par élément. Merci!
Les règles concernant la quantité de mémoire consommée dépendent de l'implémentation JVM et de l'architecture CPU (32 bits contre 64 bits par exemple).
Pour les règles détaillées de la JVM SUN, consultez mon ancien blog
Cordialement, Markus