Question

Dans la perspective .NET :

  • Qu'est-ce qu'un fuite de mémoire?
  • Comment pouvez-vous déterminer si votre application fuit ?Quels sont les effets ?
  • Comment éviter une fuite de mémoire ?
  • Si votre application présente une fuite de mémoire, celle-ci disparaît-elle à la fin du processus ou est-elle interrompue ?Ou les fuites de mémoire dans votre application affectent-elles d’autres processus sur le système même une fois le processus terminé ?
  • Et qu’en est-il du code non géré accessible via COM Interop et/ou P/Invoke ?
Était-ce utile?

La solution

La meilleure explication que j'ai vue se trouve dans le chapitre 7 du livre gratuit Livre électronique sur les fondements de la programmation.

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

Vous saurez que vous avez des fuites lorsque vous commencez à recevoir des OutOfMemoryExceptions ou que votre utilisation de la mémoire dépasse ce à quoi vous vous attendez (Mon Perf a de jolis compteurs de mémoire).

Compréhension .FILETLe modèle de mémoire de est votre meilleur moyen de l'éviter.Plus précisément, comprendre comment fonctionne le ramasse-miettes et comment fonctionnent les références – encore une fois, je vous renvoie au chapitre 7 du livre électronique.Soyez également conscient des pièges courants, le plus courant étant probablement les événements.Si objet UN est inscrit à un événement sur l'objet B, puis objecter UN restera jusqu'à ce que l'objet B disparaît parce que B contient une référence à UN.La solution est de désinscrire 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 rouges, JetBrains dotMemory, profileur de membres sont de très bons choix, ou vous pouvez utiliser le texte uniquement WinDbg et SOS, mais je recommanderais fortement un produit commercial/visuel à moins que vous ne soyez un vrai gourou).

Je pense que le code non géré est sujet à ses fuites de mémoire typiques, sauf que les références partagées sont gérées par le garbage collector.Je peux me tromper sur ce dernier point.

Autres conseils

À proprement parler, 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 à celui-ci", c'est-à-dire totalement irrécupérable, ou cela pourrait signifier référencé, récupérable, inutilisé mais le programme conserve quand même les références.Seul ce dernier s'applique à .Net pour des 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 perdre des ressources de manière permanente pour ce processus.

Dans tous les cas, l’application consomme plus de mémoire que ce qui est strictement nécessaire.Les effets secondaires, en fonction de la quantité divulguée, pourraient aller d'aucun, à un ralentissement causé par une collecte excessive, à une série d'exceptions de mémoire et enfin une erreur fatale suivie d'un arrêt forcé du processus.

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

Pour la plupart des fuites, les ressources sont récupérées lorsque le processus est terminé, cependant 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 entendu, si vous disposez d'un mécanisme de communication interprocessus, la mémoire allouée à l'autre processus ne sera pas libérée tant que ce processus ne la libérera pas ou ne se terminera pas.

Je pense que les questions "qu'est-ce qu'une fuite de mémoire" et "quels sont les effets" ont déjà reçu une bonne réponse, mais je voulais ajouter quelques éléments supplémentaires sur les autres questions...

Comment comprendre si votre application fuit

Une manière intéressante consiste à ouvrir perfmon et ajoutez des traces pour # octets dans tous les tas et #Collections Gen 2 , dans chaque cas en regardant uniquement votre processus.Si l’exercice d’une fonctionnalité particulière entraîne une augmentation du nombre total d’octets et que la mémoire reste allouée après la prochaine collecte Gen 2, vous pourriez dire que la fonctionnalité perd de la mémoire.

Comment empêcher

D'autres bons avis ont été donnés.J'ajouterais simplement que peut-être le le plus souvent négligé La cause 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 et empêchera donc la collecte même après la disparition de toutes les autres références.N'oubliez jamais de détacher les gestionnaires d'événements (en utilisant le -= syntaxe en C#).

La fuite disparaît-elle à la fin du processus, et qu’en est-il de l’interopérabilité COM ?

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

Je définirais les fuites de mémoire comme un objet ne libérant pas toute la mémoire allouée une fois son exécution terminée.J'ai découvert que cela peut se produire dans votre application si vous utilisez l'API Windows et COM (c'est-à-direcode non géré qui contient un bug ou n'est pas géré correctement), dans le framework et dans les composants tiers.J'ai également constaté que ne pas faire le ménage après avoir utilisé certains objets comme des stylos peut être à l'origine du problème.

J'ai personnellement souffert d'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.(Le MOO peut aussi provenir de l'épinglage, voir Épingler 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 à l'origine de cette erreur, le seul moyen est de profiler votre application.

J'essaierais également de garantir ce qui suit :

a) Tout ce qui implémente Idisposable est supprimé soit à l'aide d'un bloc final, soit de l'instruction using, cela inclut les pinceaux, les stylos, etc. (certaines personnes prétendent qu'il faut tout définir sur rien en plus)

b) Tout ce qui a une méthode close est fermé à nouveau en utilisant enfin ou l'instruction using (bien que j'ai trouvé que using ne se ferme pas toujours selon que vous avez déclaré l'objet en dehors de l'instruction using)

c) Si vous utilisez des API de code/Windows non gérées, celles-ci seront traitées correctement par la suite.(certains ont des méthodes de nettoyage pour libérer des ressources)

J'espère que cela t'aides.

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

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

Ces articles décrivent comment créer un vidage 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 déterminer d'où elle vient.

Microsoft dispose également d'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

Utilisation du profileur CLR de Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en est un excellent moyen de déterminer quels objets contiennent de la mémoire, quel flux d'exécution conduit à la création de ces objets, et également de surveiller quels objets se trouvent à quel endroit du tas (fragmentation, LOH, etc.).

La meilleure explication du fonctionnement du garbage collector se trouve chez Jeff Richters. CLR via C# livre, (Ch.20).La lecture de ceci donne une excellente base pour comprendre comment les objets persistent.

L’une des causes les plus courantes d’enracinement accidentel d’objets est la connexion d’événements en dehors d’une classe.Si vous connectez un événement externe

par exemple.

SomeExternalClass.Changed += new EventHandler(HandleIt);

et oubliez de vous y décrocher lorsque vous en disposez, alors SomeExternalClass a une référence à votre classe.

Comme mentionné ci-dessus, le Profileur de mémoire SciTech est excellent pour vous montrer les racines des objets dont vous soupçonnez une fuite.

Mais il existe également 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 une fois attaché) :

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

Maintenant, faites quelque chose qui, selon vous, éliminera les objets de ce type (par ex.fermer une fenêtre).C'est pratique ici d'avoir un bouton de débogage quelque part qui s'exécutera System.GC.Collect() deux-trois fois.

Puis cours !dumpheap -stat -type <TypeName> encore.Si le nombre n'a pas diminué, ou n'a pas diminué autant que prévu, vous disposez alors d'une base pour une enquête plus approfondie.(J'ai reçu cette astuce lors d'un séminaire donné par Ingo Rammer).

Je suppose que dans un environnement géré, une fuite consisterait à conserver une référence inutile à une grande partie de la mémoire.

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

Une fuite de mémoire se produit lorsque vous vous attachez à une ressource et ne la lâchez pas.Vous pouvez le faire à la fois en codage géré et non géré.

Concernant .NET et d'autres outils de programmation, des idées ont été avancées sur le garbage collection et d'autres moyens de minimiser les situations susceptibles de provoquer une fuite de votre application.Mais la meilleure méthode pour éviter les fuites de mémoire est de comprendre votre modèle de mémoire sous-jacent et son fonctionnement sur la plate-forme que vous utilisez.

Croire que GC et autres magies nettoieront vos dégâts est le chemin le plus court vers les fuites de mémoire, et sera difficile à retrouver plus tard.

Lorsque vous codez de manière non gérée, vous vous assurez normalement de nettoyer, vous savez que les ressources dont vous vous emparez seront de votre responsabilité de nettoyer, et non de celle du concierge.

Dans .NET, en revanche, beaucoup de gens pensent que le GC va tout nettoyer.Eh bien, cela en fait pour vous, mais vous devez vous assurer qu'il en est ainsi..NET englobe beaucoup de choses, vous ne savez donc pas toujours si vous avez affaire à une ressource gérée ou non, et vous devez vous assurer de ce à quoi vous avez affaire.La gestion des polices, des ressources GDI, de l'Active Directory, des bases de données, etc. est généralement une chose à laquelle vous devez faire attention.

En termes gérés, je mettrai mon cou sur la ligne pour dire qu'il disparaît une fois que le processus sera tué / supprimé.

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

Si vous ouvrez ensuite un autre onglet dans le navigateur, surfez sur un site, puis fermez l'onglet qui hébergeait l'autre page à l'origine de la fuite du navigateur, pensez-vous que le navigateur libérera de la mémoire ?Ce n'est pas le cas avec IE.Sur mon ordinateur, IE consommera facilement 1 Go de mémoire en peu de temps (environ 3 à 4 jours) si j'utilise Google Reader.Certaines pages d'information sont encore pires.

Je suppose que dans un environnement géré, une fuite serait de garder une référence inutile à une grande partie de mémoire autour.

Absolument.De plus, ne pas utiliser la méthode .Dispose() sur des objets jetables, le cas échéant, peut provoquer 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 correctement IDisposable, vous pourriez provoquer des fuites de mémoire pour les utilisateurs de votre classe.

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

Si vous perdez suffisamment de mémoire, le système d'exploitation peut décider de résoudre le problème en votre nom.

Je suis d'accord avec Bernard quant à ce que serait dans .net ce que serait une fuite de mémoire.

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 pouvez dire qu'elle a une fuite.

En termes de gestion, je mettrai mon cou en jeu pour dire que cela disparaît une fois le processus tué/supprimé.

Le code non géré est sa propre bête et s'il existe une fuite, il suivra un mem standard.définition de fuite.

Gardez également à l’esprit que .NET comporte deux tas, l’un étant le tas d’objets volumineux.Je crois que des objets d'environ 85 000 ou plus sont placés 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 de rechercher quelles sont les règles exactes.

En ce qui concerne la récupération de la mémoire à la fin du processus, à moins que vous n'exécutiez Win98 ou son équivalent, tout est restitué au système d'exploitation à la fin.Les seules exceptions concernent les éléments ouverts entre processus et un autre processus ayant toujours la ressource ouverte.

Les objets COM peuvent cependant être délicats.Si vous utilisez toujours le IDispose modèle, vous serez en sécurité.Mais j'ai rencontré quelques assemblys d'interopérabilité qui implémentent IDispose.La clé ici est d'appeler Marshal.ReleaseCOMObject quand tu auras fini.Les objets COM utilisent toujours le comptage de références COM standard.

j'ai trouvé Profileur de mémoire .Net une très bonne aide pour trouver des fuites de mémoire dans .Net.Ce n'est pas gratuit comme Microsoft CLR Profiler, mais il est plus rapide et plus pertinent à mon avis.UN

Une définition est la suivante : Impossible de libérer la mémoire inaccessible, qui ne peut plus être allouée à un nouveau processus lors de l'exécution du processus d'allocation.Il peut principalement être guéri en utilisant des techniques GC ou détecté par des outils automatisés.

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.

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