Comment les tableaux Integer sont-ils stockés en interne, dans la JVM?

StackOverflow https://stackoverflow.com/questions/76549

  •  09-06-2019
  •  | 
  •  

Question

Un tableau d'ints en java est stocké sous forme de bloc de valeurs 32 bits en mémoire. Comment un tableau d'objets Integer est-il stocké? c'est-à-dire

int[] vs. Integer[]

J'imaginerais que chaque élément du tableau Integer est une référence à un objet Integer et que l'objet Integer comporte des frais généraux de stockage d'objet, comme n'importe quel autre objet.

J'espère cependant que la machine virtuelle Java fait preuve d'intelligence magique sous le capot, étant donné que les entiers sont immuables et le stocke comme un ensemble d'ints.

Mon espoir est-il terriblement naïf? Un tableau entier est-il beaucoup plus lent qu'un tableau entier dans une application où chaque dernière once de performances compte?

Était-ce utile?

La solution

Aucune machine virtuelle à ma connaissance ne stockera un tableau Integer [] comme un tableau int [] pour les raisons suivantes:

  1. Il peut y avoir des null objets entiers dans le tableau et il ne vous reste plus de bits pour l'indiquer dans un tableau int. La VM peut cependant stocker ces informations 1 bit par emplacement de tableau dans un tableau de bits masqué.
  2. Vous pouvez synchroniser les éléments d'un tableau Integer. Ceci est beaucoup plus difficile à surmonter en tant que premier point, car vous devriez stocker un objet de contrôle pour chaque emplacement de tableau.
  3. Les éléments d'Integer [] peuvent être comparés pour l'identité. Vous pouvez par exemple créer deux objets Integer avec la valeur 1 via nouveau , les stocker dans des logements de tableau différents, puis les récupérer et les comparer via ==. Cela doit conduire à false, il vous faudra donc stocker ces informations quelque part. Ou vous gardez une référence à l'un des objets Integer quelque part et vous l'utilisez à des fins de comparaison. Vous devez vous assurer qu'une des comparaisons == est fausse et une vraie. Cela signifie que l'ensemble du concept d'identité d'objet est assez difficile à gérer pour le tableau optimisé Integer.
  4. Vous pouvez convertir un entier [] en un exemple. Object [] et transmettez-le aux méthodes qui n'attendent qu'un objet []. Cela signifie que tout le code qui gère Object [] doit maintenant être capable de gérer également l'objet Integer [] spécial, ce qui le rend plus lent et plus volumineux.

Compte tenu de tout cela, il serait probablement possible de créer un Integer [] spécial, qui économise un peu de place par rapport à une implémentation naïve , mais la complexité supplémentaire affectera probablement de nombreux autres. code, ce qui le ralentit à la fin.

Les frais généraux liés à l'utilisation d'Integer [] à la place de int [] peuvent être très volumineux dans le temps et l'espace. Sur une machine virtuelle 32 bits typique, un objet Integer consomme 16 octets (8 octets pour l’en-tête de l’objet, 4 pour la charge utile et 4 octets supplémentaires pour l’alignement), alors que Integer [] utilise autant d’espace que int []. Dans les VM 64 bits (utilisant des pointeurs 64 bits, ce qui n'est pas toujours le cas), un objet Integer consomme 24 octets (16 pour l'en-tête, 4 pour la charge utile et 4 pour l'alignement). De plus, un emplacement dans Integer [] utilisera 8 octets au lieu de 4 comme dans int []. Cela signifie que vous pouvez vous attendre à une surcharge de 16 à 28 octets par emplacement, ce qui est un facteur de 4 à 7 par rapport aux tableaux ordinaires.

Les performances peuvent également être importantes pour deux raisons principales:

  1. Étant donné que vous utilisez plus de mémoire, vous mettez beaucoup de pression sur le sous-système de mémoire, ce qui le rend plus susceptible d'avoir des erreurs de cache dans le cas d'Integer []. Par exemple, si vous parcourez le contenu de int [] de manière linéaire, la plupart des entrées du cache seront déjà extraites lorsque vous en aurez besoin (étant donné que la présentation est également linéaire). Toutefois, dans le cas du tableau Integer, les objets Integer eux-mêmes peuvent être dispersés de manière aléatoire dans le tas, ce qui empêche le cache de deviner où la prochaine référence en mémoire sera dirigée.
  2. La garbage collection doit faire beaucoup plus de travail à cause de la mémoire supplémentaire utilisée et du fait qu'elle doit analyser et déplacer chaque objet Integer séparément, alors que dans le cas de int [], il ne s'agit que d'un objet et du contenu de l'objet. ne doit pas être analysé (ils ne contiennent aucune référence à d'autres objets).

Pour résumer, utiliser un int [] dans les tâches critiques de performance sera à la fois beaucoup plus rapide et efficace en termes de mémoire que d’utiliser un tableau Integer dans les VM actuelles et il est peu probable que cela change beaucoup dans un proche avenir.

Autres conseils

John Rose travaille sur le fixnums de la machine virtuelle pour résoudre ce problème.

Je pense que votre espoir est terriblement naïf. Plus précisément, il doit traiter le problème qu'Integer peut potentiellement être nul, alors que int ne peut pas l'être. C’est une raison suffisante pour stocker le pointeur d’objet.

Cela dit, le pointeur d’objet réel sera vers une instance int immuable, notamment pour un sous-ensemble sélectionné d’entiers.

Cela ne sera pas beaucoup plus lent, mais parce qu'un entier [] doit accepter "null". comme une entrée et que int [] n'est pas obligé, il y aura une certaine quantité de comptabilité impliquée, même si Integer [] est soutenu par un int [].

Donc, si chaque dernière once de performance compte, l'utilisateur int []

La raison pour laquelle Integer peut être null, alors que int ne le peut pas, c’est qu’Integer est un objet Java à part entière, avec l’ensemble des frais généraux inclus. Il y a de la valeur à cela puisque vous pouvez écrire

Integer foo = new Integer();
foo = null; 

ce qui est bien pour dire que foo aura une valeur, mais ce n'est pas encore le cas.

Une autre différence est que int n’effectue aucun calcul de dépassement de capacité. Par exemple,

int bar = Integer.MAX_VALUE;
bar++;

augmentera joyeusement la barre et vous obtiendrez un nombre très négatif, ce qui n’est probablement pas ce que vous vouliez au départ.

foo = Integer.MAX_VALUE;
foo++;

va se plaindre, ce qui, je pense, serait un meilleur comportement.

Un dernier point est qu'Integer, étant un objet Java, entraîne avec lui la surcharge de l'espace d'un objet. Je pense que quelqu'un d'autre aura peut-être besoin de sonner ici, mais je pense que chaque objet consomme 12 octets pour la surcharge, puis l'espace pour le stockage de données lui-même. Si vous aimez les performances et l’espace, je me demande si Integer est la bonne solution.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top