Anatomie d'une "fuite de mémoire"

Dans la perspective .NET:

  • Qu'est-ce qu'une fuite de mémoire ?
  • Comment pouvez-vous déterminer si votre application fuit? Quels sont les effets?
  • Comment pouvez-vous éviter une fuite de mémoire?
  • Si votre application a une fuite de mémoire, est-ce qu'elle disparaît quand le processus se termine ou est tué? Ou les fuites de mémoire dans votre application affectent-elles d'autres processus sur le système même après la fin du processus?
  • Et qu'en est-il du code non géré accédé via COM Interop et / ou P / Invoke?
0
ajouté édité
Vues: 51

15 Réponses

Je définirais les fuites de mémoire comme un objet ne libérant pas toute la mémoire allouée après qu'il a terminé. J'ai trouvé que cela peut se produire dans votre application si vous utilisez Windows API et COM (c'est-à-dire du code non géré qui contient un bogue ou qui n'est pas géré correctement), dans le framework et dans des composants tiers. J'ai également trouvé ne pas se ranger après l'utilisation de certains objets comme des stylos peuvent causer le problème.

J'ai personnellement souffert des exceptions de mémoire insuffisante qui peuvent être causées mais ne sont pas exclusives aux fuites de mémoire dans les applications dot net. (OOM peut également provenir de l'épinglage voir Pinning Artical ) . Si vous n'obtenez pas d'erreurs de MOO ou si vous devez confirmer s'il s'agit d'une fuite de mémoire, le seul moyen est de profiler votre application.

Je voudrais également essayer et assurer ce qui suit:

a) Tout ce qui implémente Idisposable est disposé soit en utilisant un bloc finally, soit en utilisant l'instruction qui inclut les pinceaux, les stylos etc. (certaines personnes prétendent tout mettre à néant en plus)

b) Tout ce qui a une méthode close est refermé à l'aide de finally ou de l'instruction using (bien que j'ai trouvé que l'utilisation ne se ferme pas toujours si vous avez déclaré l'objet en dehors de l'instruction using)

c) Si vous utilisez des API code / windows non gérées, celles-ci sont traitées correctement après. (certains ont des méthodes de nettoyage pour libérer des ressources)

J'espère que cela t'aides.

0
ajouté

Je suppose que dans un environnement géré, une fuite consisterait à garder une référence inutile à un gros morceau de mémoire.

0
ajouté

Je serai d'accord avec Bernard quant à .net ce que serait une fuite de mem.

Vous pouvez profiler votre application pour voir son utilisation de la mémoire, et déterminer que si elle gère beaucoup de mémoire alors qu'elle ne devrait pas l'être, vous pourriez dire qu'il y a une fuite.

En termes gérés, je vais mettre mon cou sur la ligne pour dire qu'il disparaît une fois le processus est tué / enlevé.

Le code non managé est sa propre bête et si une fuite existe dedans, elle suivra un mem standard. définition de fuite.

0
ajouté

Je suppose que dans un environnement géré, un   fuite serait vous garder un   référence inutile à un gros morceau   de la mémoire autour.

Absolument. De même, ne pas utiliser la méthode .Dispose() sur des objets jetables, le cas échéant, peut entraîner des fuites de mémoire. La façon la plus simple de le faire est d'utiliser un bloc using car il exécute automatiquement .Dispose() à la fin:

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

Et si vous créez une classe qui utilise des objets non gérés, si vous n'implémentez pas IDisposable correctement, vous risquez de provoquer des fuites de mémoire pour les utilisateurs de votre classe.

0
ajouté

Strictement parlant, une fuite de mémoire consomme de la mémoire qui n'est plus utilisée par le programme.

"N'est plus utilisé" a plus d'une signification, cela pourrait signifier "plus de référence", c'est-à-dire totalement irrécupérable, ou cela pourrait signifier, référencé, récupérable, inutilisé mais le programme garde quand même les références. Seul le dernier s'applique à .Net pour les objets parfaitement gérés . Cependant, toutes les classes ne sont pas parfaites et, à un moment donné, une implémentation sous-jacente non gérée pourrait laisser des ressources permanentes pour ce processus.

Dans tous les cas, l'application consomme plus de mémoire que nécessaire. Les effets des côtés, en fonction de la fuite d'ammount, pourraient passer de zéro, au ralentissement provoqué par une collecte excessive, à une série d'exceptions de mémoire et finalement à une erreur fatale suivie d'une fin de processus forcé.

Vous savez qu'une application a un problème de mémoire lorsque la surveillance indique que de plus en plus de mémoire est allouée à votre processus après chaque cycle de récupération de place . Dans ce cas, soit vous gardez trop de mémoire, soit une implémentation non gérée sous-jacente fuit.

Pour la plupart des fuites, les ressources sont récupérées lorsque le processus est terminé, mais certaines ressources ne sont pas toujours récupérées dans certains cas précis, les poignées de curseur GDI sont connues pour cela. Bien sûr, si vous avez un mécanisme de communication interprocessus, la mémoire allouée dans l'autre processus ne sera pas libérée jusqu'à ce que ce processus le libère ou se termine.

0
ajouté

La meilleure explication que j'ai vue se trouve dans le chapitre 7 de la gratuit rel="noreferrer"> Fondations de la programmation ebook .

Fondamentalement, dans .NET, une fuite de mémoire se produit lorsque les objets référencés sont enracinés et ne peuvent donc pas être récupérés. Cela se produit accidentellement lorsque vous maintenez des références au-delà de la portée prévue.

Vous saurez que vous avez des fuites lorsque vous commencez à sortir des exceptions ou que votre utilisation de la mémoire va au-delà de ce que vous attendez (perfmon a de jolis compteurs de mémoire).

Comprendre le modèle de mémoire de .NET est votre meilleur moyen de l'éviter. Plus précisément, comprendre comment fonctionne le garbage collector et comment les références fonctionnent (encore une fois, je vous renvoie au chapitre 7 de l'ebook). Aussi, soyez conscient des pièges courants, probablement les événements les plus courants. Si l'objet A est enregistré dans un événement sur l'objet B, alors l'objet A reste affiché jusqu'à ce que l'objet B disparaisse parce que B contient une référence à A. La solution consiste à annuler l'enregistrement de vos événements lorsque vous avez terminé.

Bien sûr, un bon profil de mémoire vous permettra de voir vos graphiques d'objets et d'explorer l'imbrication / référencement de vos objets pour voir d'où viennent les références et quel objet racine est responsable ( profil de fourmis rouge-gate , JetBrains dotMemory, memprofiler sont vraiment de bons choix, ou vous pouvez utiliser le windbg et le sos, mais je vous recommande fortement un produit commercial / visuel, sauf si vous êtes un vrai gourou).

Je crois que le code non géré est sujet à des fuites de mémoire typiques de code non-émargé, sauf que les références partagées entre les deux sont gérées par le garbage collector. Peut-être tort à propos de ce dernier point.

0
ajouté
@Jeffry c'est une manière non conventionnelle de décrire ce qui se passe et je l'aime!
ajouté l'auteur Exitos, source
@kyoryu: Comment un objet se racine?
ajouté l'auteur Andrei Rînea, source
Oh tu aimes le livre, n'est-ce pas? J'ai vu l'auteur apparaître sur stackoverflow de temps en temps.
ajouté l'auteur Johnno Nolan, source
Certains objets .NET peuvent aussi s'enraciner et devenir irrécupérables. Tout ce qui est IDisposable doit être éliminé à cause de cela.
ajouté l'auteur kyoryu, source
@Andrei: Je pense qu'un thread en cours d'exécution est peut-être le meilleur exemple d'un objet qui s'auto-enracine. Un objet qui met une référence à lui-même dans un emplacement statique non public (comme s'abonner à un événement statique ou implémenter un singleton par initialisation de champ statique) peut aussi s'être enraciné, puisqu'il n'y a pas de moyen évident de ... um ... "le déraciner" de son mouillage.
ajouté l'auteur Jeffrey Hantin, source
Le code non géré / natif est généralement plus vulnérable aux fuites physiques, mais pas tellement celles qui sont logiques / enracinées. C'est parce que si nous avons une de ces ressources enracinées qui ne sont pas déracinées, typiquement elles finiront par être explicitement détruites de toute façon dans un langage comme C. L'erreur qui se produit dans ces cas est plus souvent le pointeur qui pend et souvent de segfault associé à l'accès. Bien que cela soit parfois préférable à l'utilisation de cette ressource enracinée, il suffit de consommer de la mémoire jusqu'à ce que l'application soit
ajouté l'auteur Team Upvote, source

Toutes les fuites de mémoire sont résolues par la fin du programme.

Leak assez de mémoire et le système d'exploitation peut décider de résoudre le problème en votre nom.

0
ajouté

Si vous avez besoin de diagnostiquer une fuite de mémoire dans .NET, vérifiez ces liens:

http: // msdn .microsoft.com / fr-fr / magazine / cc163833.aspx

http: // msdn .microsoft.com / fr-fr / magazine / cc164138.aspx

Ces articles décrivent comment créer un vidage de mémoire de votre processus et comment l'analyser afin que vous puissiez d'abord déterminer si votre fuite est non gérée ou gérée, et si elle est gérée, comment comprendre d'où elle vient.

Microsoft a également un outil plus récent pour aider à générer des vidages sur incident, pour remplacer ADPlus, appelé DebugDiag.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en

0
ajouté

Using CLR Profiler from Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en is a great way to determine which objects are holding memory, what execution flow leads to the creation of these objects, and also monitoring which objects live where on the heap (fragmentation, LOH, etc.).

0
ajouté

Gardez également à l'esprit que .NET a deux tas, l'un étant le tas d'objets volumineux. Je crois que des objets d'environ 85k ou plus sont mis sur ce tas. Ce tas a des règles de durée de vie différentes de celles du tas normal.

Si vous créez de grandes structures de mémoire (dictionnaire ou liste) il serait prudent d'aller chercher quelles sont les règles exactes.

En ce qui concerne la récupération de la mémoire à la fin du processus, sauf si vous exécutez Win98 ou ses équivalents, tout est renvoyé à l'OS à la fin du processus. Les seules exceptions sont les éléments qui sont ouverts entre processus et un autre processus a toujours la ressource ouverte.

Les objets COM peuvent être difficiles. Si vous utilisez toujours le motif IDispose , vous serez en sécurité. Mais j'ai parcouru quelques assemblages interopératifs qui implémentent IDispose . La clé ici est d'appeler Marshal.ReleaseCOMObject lorsque vous avez terminé. Les objets COM utilisent toujours le comptage de références COM standard.

0
ajouté

J'ai trouvé .NET Memory Profiler une très bonne aide lors de la recherche de fuites de mémoire dans. Net. Ce n'est pas gratuit comme Microsoft CLR Profiler, mais c'est plus rapide et plus à mon avis. UNE

0
ajouté

Je pense que les questions "qu'est-ce qu'un problème de mémoire" et "quels sont les effets" ont déjà été bien répondues, mais je voulais ajouter quelques autres choses sur les autres questions ...

Comment comprendre si votre application présente des fuites

Une façon intéressante est d'ouvrir perfmon et d'ajouter des traces pour # octets dans tous les tas et # Gen 2 collections , dans chaque cas en regardant votre processus. Si l'utilisation d'une fonctionnalité particulière entraîne une augmentation du nombre total d'octets et que cette mémoire reste allouée après la prochaine collecte Gen 2, vous pouvez dire que la fonction perd de la mémoire.

Comment éviter

D'autres bonnes opinions ont été données. J'ajouterais simplement que la cause la plus souvent négligée des fuites de mémoire .NET est d'ajouter des gestionnaires d'événements aux objets sans les supprimer. Un gestionnaire d'événements attaché à un objet est une forme de référence à cet objet, ce qui empêchera la collecte même après que toutes les autres références auront disparu. Rappelez-vous toujours de détacher les gestionnaires d'événements (en utilisant la syntaxe - = en C #).

La fuite disparaît-elle à la fermeture du processus? Qu'en est-il de COM interop?

Lorsque votre processus se termine, toute la mémoire mappée dans son espace adresse est récupérée par le système d'exploitation, y compris les objets COM servis à partir de DLL. Comparativement, les objets COM peuvent être servis à partir de processus séparés. Dans ce cas, lorsque votre processus se termine, vous pouvez toujours être responsable de la mémoire allouée dans les processus serveur COM que vous avez utilisés.

0
ajouté

La meilleure explication du fonctionnement du garbage collector est dans Jeff Richters CLR via C# livre, (Ch. 20). La lecture donne une bonne base pour comprendre comment les objets persistent.

L'une des causes les plus fréquentes de l'enracinement des objets est accidentellement liée à des événements hors classe. Si vous connectez un événement externe

par exemple.

SomeExternalClass.Changed += new EventHandler(HandleIt);

et oubliez de le décrocher quand vous disposez, alors SomeExternalClass a un ref à votre classe.

Comme mentionné ci-dessus, le profileur de mémoire SciTech est excellent pour vous montrer les racines des objets que vous suspectez fuient.

Mais il existe aussi un moyen très rapide de vérifier un type particulier: il suffit d'utiliser WnDBG (vous pouvez même l'utiliser dans la fenêtre immédiate de VS.NET quand vous êtes connecté):

.loadby sos mscorwks
!dumpheap -stat -type 

Now do something that you think will dispose the objects of that type (par exemple. close a window). It's handy here to have a debug button somewhere that will run System.GC.Collect() a couple of times.

Then run !dumpheap -stat -type again. If the number didn't go down, or didn't go down as much as you expect, then you have a basis for further investigation. (I got this tip from a seminar given by Ingo Rammer).

0
ajouté

Pourquoi les gens pensent-ils qu'une fuite de mémoire dans .NET n'est pas la même que toute autre fuite?

Une fuite de mémoire est lorsque vous attachez à une ressource et ne le laissez pas partir. Vous pouvez le faire à la fois dans le codage géré et dans le codage non géré.

En ce qui concerne .NET, et d'autres outils de programmation, il y a eu des idées sur la collecte des déchets, et d'autres façons de minimiser les situations qui feront fuir votre application. Mais la meilleure méthode pour éviter les fuites de mémoire est que vous devez comprendre votre modèle de mémoire sous-jacent, et comment les choses fonctionnent, sur la plate-forme que vous utilisez.

Croire que GC et d'autres magiques vont nettoyer votre gâchis est le moyen le plus rapide de fuites de mémoire, et sera difficile à trouver plus tard.

Lors du codage non géré, vous devez normalement vous assurer de nettoyer, vous savez que les ressources que vous prenez, seront votre responsabilité de nettoyer, pas le concierge.

En .NET d'un autre côté, beaucoup de gens pensent que le GC va tout nettoyer. Eh bien, il en fait pour vous, mais vous devez vous assurer qu'il en est ainsi. .NET encapsule beaucoup de choses, donc vous ne savez pas toujours si vous avez affaire à une ressource gérée ou non, et vous devez vous assurer de ce que vous avez à faire. La gestion des polices, des ressources GDI, du répertoire actif, des bases de données, etc. est généralement ce dont vous avez besoin.

En termes de gestion, je vais mettre mon cou   la ligne pour dire qu'il disparaît une fois   le processus est tué / enlevé.

Je vois beaucoup de gens qui ont cela, et j'espère vraiment que cela prendra fin. Vous ne pouvez pas demander à l'utilisateur de terminer votre application pour nettoyer votre désordre! Jetez un oeil à un navigateur, qui peut être IE, FF, etc, puis ouvrez, disons, Google Reader, laissez-le rester pendant quelques jours, et regardez ce qui se passe.

Si vous ouvrez ensuite un autre onglet dans le navigateur, naviguez jusqu'à un site, puis fermez l'onglet qui hébergeait l'autre page qui a provoqué la fuite du navigateur, pensez-vous que le navigateur va libérer la mémoire? Pas si avec IE. Sur mon ordinateur IE va facilement manger 1 Gio de mémoire dans un court laps de temps (environ 3-4 jours) si j'utilise Google Reader. Certaines pages sont encore pire.

0
ajouté

One definition is: Unable to release unreachable memory, which can no longer be allocated to new process during execution of allocating process. It can mostly be cured by using GC techniques or detected by automated tools.

Pour plus d'informations, s'il vous plaît visitez http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html .

0
ajouté