Libérer explicitement la mémoire en c#
J'ai créé une application c# qui utilise 150 Mo de Mémoire (Octets privés), principalement en raison d'un gros dictionnaire:
Dictionary<string, int> Txns = new Dictionary<string, int>();
Je me demandais comment libérer cette mémoire. J'ai essayé ceci:
Txns = null;
GC.Collect();
Mais cela ne semble pas faire beaucoup de brèche dans Mes octets privés - ils passent de 155mb à 145mb. Des indices?
Merci
- modifier -
OK, j'ai plus de chance avec ce code (il ramène les octets privés à 50 Mo), mais Pourquoi?
Txns.Clear(); // <- makes all the difference
Txns = null;
GC.Collect();
- modifier -
OK pour tous ceux qui disent ' ne pas utiliser GC.collect', assez juste (Je ne vais pas en débattre, à part dire que vous pouvez voir mon arrière-plan C passer), mais cela ne répond pas vraiment à ma question: Pourquoi le garbage collector ne libère-t-il la mémoire que si j'efface d'abord la liste des transactions? Ne devrait-il pas libérer la mémoire de toute façon, puisque le dictionnaire a été déréférencé?
9 réponses
Les octets privés reflètent l'utilisation de la mémoire du processus. Lorsque des objets sont collectés, le segment de mémoire associé peut ou non être libéré vers le système d'exploitation. Le CLR gère la mémoire au niveau du système D'exploitation et puisque l'allocation et la libération de la mémoire ne sont pas libres, il n'y a aucune raison de libérer chaque morceau de mémoire immédiatement car il est probable que l'application demandera plus de mémoire plus tard.
Si vous appelez GC.Collect() commence à faire son travail, mais il retourne immédiatement, il ne bloque pas, donc vous ne voyez pas son effet, si vous appelez simplement GC.WaitForPendingFinalizers () après cela, il bloquera votre application jusqu'à GC.Collect () termine son travail
Très probablement vous avez une référence cachée au dictionnaire ailleurs. Ainsi, le dictionnaire n'est pas collecté, mais si vous le Clear()
, le contenu est collecté.
Comme d'autres l'ont déjà noté, forcer le GC n'est pas recommandé. Cela pourrait conduire à pousser la mémoire dans les "générations" supérieures qui ne sont pas souvent collectées, gaspillant ainsi plus de mémoire que gagné à long terme.
Pas sûr de la mémoire si Dictionary
a un Dispose()
ou non, mais il est lié à un Clear()
. Appelez l'un ou l'autre avant de définir des références à null
.
Ensuite, laissez simplement le garbage collector faire son travail. C'est presque jamais une bonne idée d'appeler GC.Collect()
explicitement vous-même et cela pourrait même ne pas faire ce que vous voulez/avez besoin/attendez et finir par vous coûter des performances. L'analyse de Code statique (=FxCop) ne vous avertit pas avec règle de fiabilité CA2001 à ce sujet pour rien, vous savez? Il suffit de ne pas le faire à moins que vous savez vraiment ce que vous faites. Et même alors ne le faites pas. ;-)
Êtes-vous sûr que le dictionnaire est énorme? N'est-ce pas seulement 10 Mo de mémoire et le reste est pris par votre application? Question qui pourrait vous aider: avez-vous encore utilisé un profileur pour voir où la mémoire est réellement consommée...?
Modifier:
Pour être juste, définir la référence à null ne libère pas la mémoire, il assigne son conteneur à une adresse différente, dans ce cas null. Selon MSDN , l'appel à Clear()
, fait ceci: "la propriété Count est définie sur 0, et les références à d'autres objets à partir d'éléments de la collection sont également publiées. La capacité reste inchangée."
...
Vous ne devriez jamais appeler le garbage collector. Vous utilisez des objets gérés sans ressources natives, faites confiance au garbage collector pour nettoyer après vous.
Outre la taille de votre dictionnaire, vous n'avez pas à vous soucier de la mémoire, la mémoire n'est pas votre problème, c'est le problème des garbage collectors.
L'appel à Clear()
supprimera les références à tout objet contenu à l'intérieur, mais la capacité reste inchangée.
Sur une note technique, la collecte de mémoire est coûteuse et prend beaucoup de temps à fonctionner. La raison étant, non seulement le GC gère la mémoire de tas et nettoie votre tas, il défragmente aussi le tas. Il essaie de déplacer la mémoire dans des blocs contigus pour accélérer l'allocation quand un morceau de code fait une demande importante.
P. s. Quelle est la taille de votre dictionnaire que vous utilisez 155 Mo de mémoire?
Avez - vous besoin de la mémoire? La mémoire est disponible, elle n'est tout simplement pas récupérée. Vous ne devez pas effacer le dictionnaire, y tenir une référence faible et laisser le runtime faire son travail.
Si vous voulez voir vraiment en profondeur ce qui se passe, consultez le . Net Memory Profiler . Cela vous donnera une visibilité sur exactement ce qui se passe avec votre objet, quelle génération il est, quelle mémoire est utilisée par quoi et encore et encore. :)
Ok, j'ai une théorie ici... Dictionary est une collection de KeyValuePair qui est à nouveau un type de référence.
Votre dictionnaire contient ces keyValuePairs. Quand vous dites:
Txns = null
Il libère la référence 'Txns' de la collection KeyValuePair. Mais encore la mémoire réelle de 150 Mo est référencée par ces KeyValuePair et ils sont dans la portée, donc pas prêt pour la récupération de place.
Mais lorsque vous utilisez ce qui suit:
Txns.Clear();
Txns = null;
GC.Collect();
Ici, la méthode claire aussi libère les 150 Mo de données de leurs références D'objets KeyValuePair respectives. Ainsi, ces objets étaient prêts pour la collecte des ordures.
C'est juste une supposition sauvage que je fais ici. Les commentaires sont les bienvenus :)
Windows a deux événements de disponibilité de la mémoire. Je m'attends à ce que le CLR réponde à cela. S'il y a beaucoup de mémoire disponible, la chose intelligente est de ne pas exécuter le garbage collector. Donc, pour vous assurer que vous observez bien un mauvais comportement CLR, veuillez répéter ce test avec une autre application factice en utilisant un gros tas de mémoire.
Ce n'est généralement pas une bonne idée d'essayer de forcer le GC. Avez-vous vraiment besoin d'avoir tout le dictionnaire en mémoire?