Où sont stockés les champs primitifs?
Les champs primitifs sont stockés en tant que partie de l'objet instancié quelque part . La meilleure façon de voir où cela se trouve est le tas. Cependant , ce n'est pas toujours le cas. Comme décrit dans la théorie et la pratique Java: Légendes de la performance urbaine, revisitées :
Les machines virtuelles Java peuvent utiliser une technique appelée analyse d'échappement, qui leur permet de savoir que certains objets restent confinés dans un seul thread pendant toute leur durée de vie, et que cette durée de vie est limitée par la durée de vie d'un cadre de pile donné. De tels objets peuvent être alloués en toute sécurité sur la pile au lieu du tas. Mieux encore, pour les petits objets, la machine virtuelle Java peut optimiser complètement l’allocation et hisser simplement les champs de l’objet dans des registres.
Ainsi, au-delà de dire "l'objet est créé et le champ y est aussi", on ne peut pas dire si quelque chose se trouve sur le tas ou sur la pile. Notez que pour les petits objets de courte durée, il est possible que "l'objet" n'existe pas en mémoire en tant que tel et que ses champs soient placés directement dans des registres.
Le papier se termine par:
Les JVM sont étonnamment douées pour comprendre des choses que nous supposions auparavant que seul le développeur pouvait connaître. En laissant la JVM choisir entre l'allocation de pile et l'allocation de segment de mémoire au cas par cas, nous pouvons obtenir les avantages en termes de performances de l'allocation de pile sans que le programmeur ne s'embarrasse à l'idée d'allouer sur la pile ou sur le tas.
Donc, si vous avez un code qui ressemble à:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
où le ...
ne permet pas qux
de quitter cette portée, qux
peut être alloué sur la pile à la place. C’est en fait une victoire pour la machine virtuelle, car cela signifie qu’elle n’a pas besoin d’être ramassée à la poubelle - elle disparaîtra quand elle quittera la portée.
Plus d'informations sur l' analyse d'évasion sur Wikipedia. Pour ceux qui souhaitent approfondir leurs connaissances, Escape Analysis for Java d’IBM. Pour ceux qui viennent d'un monde C #, vous pouvez trouver de bonnes lectures: La pile est un détail d'implémentation et La vérité sur les types de valeur par Eric Lippert (utiles pour les types Java, de nombreux concepts et aspects étant identiques ou similaires) . Pourquoi les livres .Net parlent-ils d'allocation de mémoire pile / pile? va aussi dans cela.
Sur les pourquoi de la pile et du tas
Sur le tas
Alors, pourquoi avoir la pile ou le tas du tout? Pour les choses qui laissent de la place, la pile peut être chère. Considérons le code:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
Les paramètres font également partie de la pile. Dans le cas où vous n'avez pas de tas, vous transmettez l'ensemble des valeurs de la pile. C'est bien pour de "foo"
petites chaînes ... mais que se passerait-il si quelqu'un mettait un fichier XML énorme dans cette chaîne? Chaque appel copierait toute la chaîne sur la pile, ce qui serait une perte de temps.
Au lieu de cela, il est préférable de placer les objets qui ont une vie en dehors de la portée immédiate (passés à une autre portée, coincés dans une structure que quelqu'un d'autre maintient, etc.) dans un autre domaine appelé le tas.
Sur la pile
Vous n'avez pas besoin de la pile. On pourrait, hypothétiquement, écrire un langage qui n’utilise pas de pile (de profondeur arbitraire). Un vieux BASIC que j’ai appris dans ma jeunesse l’a fait, on ne pouvait faire que 8 niveaux d’ gosub
appels et toutes les variables étaient globales - il n’y avait pas de pile.
L'avantage de la pile est que, lorsque vous avez une variable qui existe avec une étendue, lorsque vous quittez cette étendue, ce cadre de pile est éclaté. Cela simplifie vraiment ce qui existe et ce qui ne l’est pas. Le programme passe à une autre procédure, un nouveau cadre de pile; le programme revient à la procédure et vous revenez à celui qui voit votre étendue actuelle; le programme quitte la procédure et tous les éléments de la pile sont désalloués.
Cela rend vraiment la vie facile pour la personne qui écrit le runtime pour que le code utilise une pile et un tas. Ils consistent simplement en de nombreux concepts et façons de travailler sur le code, ce qui permet à la personne qui l’écrit dans le langage d’être libérée d’y penser explicitement.
La nature de la pile signifie également qu'elle ne peut pas être fragmentée. La fragmentation de la mémoire est un réel problème avec le tas. Vous allouez quelques objets, puis ramassez un objet central, puis vous essayez de trouver de la place pour le prochain objet de grande taille à allouer. C'est le bordel. Pouvoir mettre les choses sur la pile signifie plutôt que vous n'avez pas à vous en occuper.
Quand quelque chose est ramassé
Quand quelque chose est ramassé, il est parti. Mais ce n'est que des ordures collectées car elles ont déjà été oubliées - il n'y a plus de références à l'objet dans le programme auxquelles on peut accéder à partir de l'état actuel du programme.
Je ferai remarquer qu'il s'agit d'une très grande simplification de la collecte des ordures. Il existe de nombreux éboueurs (même en Java - vous pouvez modifier ce dernier en utilisant divers indicateurs ( docs ). Ceux-ci se comportent différemment et les nuances de la façon dont chacune fait les choses sont un peu trop profondes pour cette réponse. Vous voudrez peut-être lire Java Garbage Collection Basics pour avoir une meilleure idée de comment cela fonctionne.
Cela dit, si quelque chose est alloué sur la pile, ce ne sont pas des ordures collectées dans le cadre de System.gc()
, mais elles sont désallouées lorsque le cadre de la pile apparaît. Si quelque chose se trouve sur le tas et est référencé à partir de quelque chose sur la pile, ce ne sera pas une ordure collectée à ce moment-là.
Pourquoi est-ce important?
Pour la plupart, sa tradition. Les manuels écrits, les classes de compilateur et la documentation sur divers bits font l’objet d’une grande importance pour le tas et la pile.
Cependant, les machines virtuelles d’aujourd’hui (JVM et similaires) ont tout mis en oeuvre pour tenter de cacher cela au programmeur. À moins que vous ne manquiez de l'un ou de l'autre et que vous ayez besoin de savoir pourquoi (plutôt que d'augmenter simplement l'espace de stockage de manière appropriée), peu importe.
L'objet est quelque part et il se trouve à l'endroit où il peut être consulté correctement et rapidement pendant la période de temps qu'il existe. Si c'est sur la pile ou le tas - ce n'est pas grave.