46 votes

Où sont stockés tous les membres statiques ?

Je suis en train d'apprendre comment C# gère la mémoire. Je suis bloqué sur les éléments statiques, j'ai lu de nombreux blogs et articles sur ce sujet, mais je ne trouve pas de réponse vraiment satisfaisante.

Définissons un bloc de code pour aider à trouver la réponse.

class myClass
{
    static string myStr = "Donnée de chaîne";
    static int myInt = 12;
}

Avant que vous ne partagiez votre réponse, laissez-moi partager mes découvertes que je connais sur ce sujet. N'hésitez pas à être d'accord ou en désaccord et aidez-moi à trouver la bonne réponse.

  • Static est juste pour la durée de vie.
  • Un type de référence statique (myStr) ira sur le tas, pour toute sa durée de vie.
  • Un type de valeur statique (myInt) ira sur la pile, pour toute sa durée de vie.

Ce qui me perturbe, ce sont certaines réponses que j'ai trouvées sur internet, sur ce sujet.

Confusion numéro 1:

Lorsque votre programme démarre, il charge toutes les assemblies associées dans un AppDomain. Lorsque l'assembly est chargé, tous les constructeurs statiques sont appelés, y compris les champs statiques. Ils resteront là, et la seule façon de les décharger est de décharger l'AppDomain.

Dans les lignes ci-dessus, il est explicitement mentionné que tous les éléments statiques sont stockés dans AppDomain. Alors pourquoi tout le monde sur internet dit que les éléments statiques sont stockés sur le tas/la pile ?

Confusion numéro 2:

Chaque variable statique est stockée sur le tas, que ce soit déclaré dans un type de référence ou un type de valeur.

Si chaque variable statique est stockée sur le tas. Alors pourquoi certaines personnes disent que les variables statiques de type de valeur sont stockées sur la pile ?

S'il vous plaît, aidez-moi à relier mes points pour comprendre la gestion de mémoire des variables statiques en C#. Merci beaucoup pour votre précieux temps :)

15 votes

Les gens arrivent à se confondre énormément en traînant les concepts de "pile" et "tas" en premier lieu. Ces concepts ne sont pas utiles en soi si tout ce que vous voulez savoir est la durée de vie d'un objet ou la portée d'une déclaration, qui sont des concepts beaucoup plus pertinents en C#. La collecte des déchets signifie que, 95% du temps, tout ce dont vous avez besoin de vous soucier est de savoir si un objet est vivant ou non, et un objet référencé par un champ static est vivant aussi longtemps que la classe est chargée. (Quant à son instanciation, c'est un sujet plus compliqué.) Bien sûr, ceci n'est pas une réponse.

1 votes

Parce qu'ils se trompent. Ils savent que les variables de type de valeur locale sont stockées sur la pile (alors que en fait toutes les variables locales sont stockées sur la pile. La confusion vient du fait que pour un type de référence, la variable est la référence, pas l'objet). Les variables statiques ressemblent à des membres de l'objet Type, et Type n'est pas un type de valeur. (Bien sûr, contrairement à Java, C# n'a en réalité pas de type Type pour clarifier que chaque Type est un type différent et a des membres différents)

9 votes

@Random832: Toutes les variables locales ne sont pas sur la pile. Les variables locales fermées ne se trouvent pas sur la pile. Les variables locales dans les blocs d'itérateurs ne sont pas sur la pile. Les variables locales dans les méthodes asynchrones ne sont pas sur la pile. Les variables locales enregistrées ne sont pas sur la pile. Les variables éludées ne sont pas sur la pile. Arrêtez de croire que les variables locales vont sur la pile; c'est simplement faux. Les variables locales sont appelées locales parce que leurs noms ont une portée locale, pas parce qu'elles sont stockées sur la pile.

64voto

Luaan Points 8934

Tout d'abord, notez que tout ceci est un détail d'implémentation. La seule chose garantie par le runtime est :

  • Lorsque vous demandez un champ statique, il est là
  • Un constructeur statique est exécuté à un moment donné avant que vous n'utilisiez le type

C'est à peu près tout. Tout le reste est un détail d'implémentation - la spécification ne se soucie pas de la pile, du tas ou autre. Cela dépend de l'implémentation du runtime, et un runtime valide pourrait tout mettre sur la pile, s'il le souhaite, ou sur le tas. Et n'oubliez pas les registres.

Maintenant, voyons certaines des idées fausses que vous avez déjà réussi à assimiler :

  • Le statique est juste pour la durée de vie - oui. Cela ne dit rien sur quand ou où il est stocké - juste qu'il est disponible lorsque vous en avez besoin. Un runtime conforme est libre d'utiliser n'importe quelle mémoire, ou même de ne jamais charger les champs en mémoire (par exemple en les gardant dans l'image, qui est déjà en mémoire de toute façon)
  • Le statique va sur le tas, pour la durée de vie - très probablement, oui. Mais ce n'est pas une partie de la spécification, et un runtime conforme peut le stocker n'importe où, ou même nulle part du tout, tant que les garanties adéquates sont respectées. De plus, n'oubliez pas que "pour la durée de vie" signifie "au moins pour la durée de vie de l'AppDomain"; il peut être libéré ou non lorsque le domaine est déchargé.
  • Le type de valeur statique ira sur la pile, pour la durée de vie - très probablement non. Encore une fois, un détail d'implémentation, mais la pile a des sémantiques complètement différentes de ce qui fait sens pour une valeur statique. Et le point suivant vous donnera une raison de plus:
  • Lorsque l'assemblage est chargé, tous les constructeurs statiques sont appelés, y compris les champs statiques. - Non. Il n'y a pas une telle exigence, et aucune telle garantie. Si vous comptez là-dessus, votre programme va planter (et j'ai vu cela de nombreuses fois auparavant). Encore une fois, un détail d'implémentation, mais dans les implémentations actuelles de MSCLR, les statiques ont tendance à être alloués dans un tas qui leur est propre, et un certain temps avant que le type dans lequel ils sont définis soit nécessaire. Vous pouvez facilement le constater si vous lancez une exception dans un constructeur statique - cela provoquera probablement une TypeLoadException, dans une méthode qui fait référence en premier lieu au type (inutile de dire que cela peut rendre le débogage des statiques délicat).
  • Les types de référence vont sur le tas, les types de valeur vont sur la pile. - Non. Cela confond le mécanisme avec la sémantique. La seule différence entre les deux réside dans leur sémantique - tout le reste dépend de l'implémentation. Si un runtime peut préserver les sémantiques de référence pour les types de référence sur la pile, c'est parfaitement valable. Et même avec les runtimes MSCLR actuels, les types de valeur sont toujours stockés sur le tas - chaque fois qu'ils sont boxés, ou membres d'un type de référence, par exemple.

Certains peuvent être confus. Certains ne comprennent pas la différence entre le contrat et les implémentations pratiques. Certains ne savent tout simplement pas de quoi ils parlent. J'aimerais qu'il soit facile de distinguer les uns des autres, mais ce n'est pas le cas. En cas de doute, vous pouvez consulter les spécifications C#/CLR, mais cela ne vous informe que sur le contrat, pas sur la réalité pratique.

Le but même de la gestion de mémoire gérée est que vous n'êtes pas censé vous soucier de ces détails d'implémentation. Bien sûr, comme toute abstraction, cela fuit - et il est logique de savoir comment les choses sont vraiment, jusqu'aux micro-instructions du CPU, le caching mémoire, etc., à travers toutes les différentes couches et abstractions. Mais ce n'est rien sur quoi s'appuyer - l'implémentation peut changer à tout moment, et elle l'a fait de nombreuses fois dans le passé.

0 votes

Je suis d'accord avec vous que c'est tout simplement un détail d'implémentation qui peut changer avec le temps. Donc, je conclus, AppDomain a son propre tas statique. Qui gère les membres statiques à la fois de type valeur et de type référence.

0 votes

@AliAsad Oui, en gros. Il est toujours pris en compte lors de la collecte des ordures (il peut avoir des références à des objets sur d'autres tas), mais en pratique, il n'est pas réellement collecté. De plus, notez que les chaînes de caractères sont spéciales - par défaut, toutes les chaînes de caractères littérales sont internées, donc si vos éléments statiques ont une valeur de chaîne, il est probable que la valeur elle-même soit dans un autre tas (généralement, le tas des grands objets).

0 votes

@AliAsad Et en ce qui concerne les éléments contractuels, les statistiques dans différents domaines d'applications sont nécessairement isolées. Cela ne nécessite pas que les objets soient dans des tas différents, mais cela rend certainement les choses plus pratiques :)

8voto

Amir Popovich Points 14335

Chaque fois qu'un processus est chargé dans la RAM, nous pouvons dire que la mémoire est approximativement divisée en trois zones (à l'intérieur de ce processus) : Pile, Tas, et Statique (qui, en .NET, est en fait une zone spéciale à l'intérieur de Tas connue uniquement sous le nom de Tas Haute Fréquence).

La partie statique contient les variables et méthodes "statiques". Qu'est-ce que signifie statique ? Ces méthodes et variables qui n'ont pas besoin d'une instance d'une classe pour être créées sont définies comme étant statiques

En savoir plus ici.

0 votes

Notez que ceci n'est qu'un article sur CodeProject, sans références, sans citations de spécifications, sans travail de personnes concevant ou implémentant .NET. C'est une introduction pour débutants, et c'est même clairement incorrect (les méthodes statiques sont stockées sur le tas?).

2voto

Mafii Points 5230

Il y a une instance de la classe créée, avec tous les membres statiques initialisés.

Les membres des classes statiques sont normalement stockés sur le tas, les membres des types de valeur sont normalement stockés sur la pile.

Ce n'est pas nécessairement le cas cependant, vous pouvez lire cet article pour plus d'informations.

C'est écrit par l'un des concepteurs de langage de C#, Eric Lippert.

L'article montre que, contrairement à ce que l'on croit normalement, il n'est pas certain que les types de valeur soient sur la pile et les types de référence sur le tas, mais c'est généralement le cas.

La spécification ne précise simplement pas où ils doivent être stockés.

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