Quelles stratégies et quels outils sont utiles pour rechercher des fuites de mémoire dans .NET?


152

J'ai écrit du C ++ pendant 10 ans. J'ai rencontré des problèmes de mémoire, mais ils pourraient être résolus avec un effort raisonnable.

Depuis quelques années, j'écris en C #. Je trouve que j'ai encore beaucoup de problèmes de mémoire. Ils sont difficiles à diagnostiquer et à corriger en raison de la non-détermination, et parce que la philosophie C # est que vous ne devriez pas avoir à vous soucier de telles choses lorsque vous le faites très certainement.

Un problème particulier que je trouve est que je dois explicitement éliminer et nettoyer tout dans le code. Si je ne le fais pas, alors les profileurs de mémoire n'aident pas vraiment car il y a tellement de paillettes qui flottent que vous ne pouvez pas trouver de fuite dans toutes les données qu'ils essaient de vous montrer. Je me demande si j'ai une mauvaise idée ou si l'outil que j'ai n'est pas le meilleur.

Quels types de stratégies et d'outils sont utiles pour lutter contre les fuites de mémoire dans .NET?


Le titre de votre message ne correspond pas vraiment à la question de votre message. Je vous suggère de mettre à jour votre titre.
Kevin le

Vous avez raison. Désolé, j'en avais un peu marre de la fuite actuelle que je chasse! Titre mis à jour.
Scott Langham

3
@Scott: N'en avez pas marre de .NET, ce n'est pas le problème. Votre code est.
GEOCHET

3
Oui, mon code ou les bibliothèques tierces que j'ai le plaisir d'utiliser.
Scott Langham

@Scott: Voir ma réponse. MemProfiler en vaut la peine. Son utilisation vous donnera également un tout nouveau niveau de compréhension du monde .NET GC.
GEOCHET

Réponses:


51

J'utilise MemProfiler de Scitech lorsque je soupçonne une fuite de mémoire.

Jusqu'à présent, je l'ai trouvé très fiable et puissant. Cela a sauvé mon bacon au moins une fois.

Le GC fonctionne très bien dans .NET IMO, mais comme tout autre langage ou plate-forme, si vous écrivez du mauvais code, de mauvaises choses se produisent.


3
Oui, j'ai essayé celui-ci et cela m'a aidé à découvrir des fuites délicates. Les plus grandes fuites que j'avais avérées étaient causées par des bibliothèques tierces dans du code non géré auquel elles avaient accès via interop. J'ai été impressionné par le fait que cet outil détecte des fuites dans le code non géré ainsi que dans le code géré.
Scott Langham

1
J'ai accepté cela comme réponse parce que c'est ce qui a fonctionné pour moi à la fin, mais je pense que toutes les autres réponses sont très utiles. À propos, cet outil est plus communément appelé Mem Profiler de SciTech!
Scott Langham

41

Juste pour le problème de l'oubli de se débarrasser, essayez la solution décrite dans cet article de blog . Voici l'essence:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif

Je préférerais lancer une exception au lieu de Debug.Fail
Pedro77

17

Nous avons utilisé le logiciel Ants Profiler Pro by Red Gate dans notre projet. Cela fonctionne très bien pour toutes les applications basées sur le langage .NET.

Nous avons constaté que le garbage collector .NET est très «sûr» dans son nettoyage des objets en mémoire (comme il se doit). Il garderait des objets à portée de main juste parce que nous pourrions l'utiliser dans le futur. Cela signifiait que nous devions être plus prudents sur le nombre d'objets que nous gonflions en mémoire. En fin de compte, nous avons converti tous nos objets de données en un «gonflement à la demande» (juste avant qu'un champ ne soit demandé) afin de réduire la surcharge mémoire et d'augmenter les performances.

EDIT: Voici une explication supplémentaire de ce que j'entends par «gonfler à la demande». Dans notre modèle d'objet de notre base de données, nous utilisons les propriétés d'un objet parent pour exposer le ou les objets enfants. Par exemple, si nous avions un enregistrement qui faisait référence à un autre enregistrement "détaillé" ou "recherche" sur une base individuelle, nous le structurerions comme ceci:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

Nous avons constaté que le système ci-dessus créait de réels problèmes de mémoire et de performances lorsqu'il y avait beaucoup d'enregistrements en mémoire. Nous sommes donc passés à un système où les objets n'étaient gonflés que lorsqu'ils étaient demandés, et les appels à la base de données n'étaient effectués que lorsque cela était nécessaire:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Cela s'est avéré beaucoup plus efficace car les objets étaient conservés hors de la mémoire jusqu'à ce qu'ils soient nécessaires (la méthode Get a été accédée). Il a fourni une très grande amélioration des performances en limitant les accès à la base de données et un énorme gain d'espace mémoire.


Je seconde ce produit. C'était l'un des meilleurs profileurs que j'ai utilisés.
Gord

J'ai trouvé que le profileur était assez bon pour examiner les problèmes de performances. Cependant, les outils d'analyse de la mémoire étaient assez pauvres. J'ai trouvé une fuite avec cet outil, mais il était inutile de m'aider à identifier la cause de la fuite. Et cela ne vous aide pas du tout si la fuite se trouve dans du code non géré.
Scott Langham

Ok, la nouvelle version 5.1, est bien meilleure. Il est préférable de vous aider à trouver la cause de la fuite (même si - il y a encore quelques problèmes avec cela que ANTS m'a dit qu'ils vont résoudre dans la prochaine version). Cependant, ne fait toujours pas de code non géré, mais si vous ne vous souciez pas du code non géré, c'est maintenant un très bon outil.
Scott Langham

7

Vous devez toujours vous soucier de la mémoire lorsque vous écrivez du code managé, sauf si votre application est triviale. Je vais suggérer deux choses: tout d'abord, lisez CLR via C # car cela vous aidera à comprendre la gestion de la mémoire dans .NET. Deuxièmement, apprenez à utiliser un outil comme CLRProfiler (Microsoft). Cela peut vous donner une idée de la cause de votre fuite de mémoire (par exemple, vous pouvez jeter un œil à la fragmentation de votre tas d'objets volumineux)


Oui. CLRPRofiler est plutôt cool. Cela peut devenir un peu explosif avec des informations lorsque vous essayez de creuser dans la vue qu'il vous donne des objets alloués, mais tout est là. C'est définitivement un bon point de départ, d'autant plus que c'est gratuit.
Scott Langham

6

Utilisez-vous du code non géré? Si vous n'utilisez pas de code non géré, selon Microsoft, les fuites de mémoire au sens traditionnel du terme ne sont pas possibles.

Cependant, la mémoire utilisée par une application peut ne pas être libérée, de sorte que l'allocation de mémoire d'une application peut augmenter tout au long de la vie de l'application.

De Comment identifier les fuites de mémoire dans le Common Language Runtime sur Microsoft.com

Une fuite de mémoire peut se produire dans une application .NET Framework lorsque vous utilisez du code non managé dans le cadre de l'application. Ce code non managé peut perdre de la mémoire et le runtime .NET Framework ne peut pas résoudre ce problème.

En outre, un projet peut sembler uniquement avoir une fuite de mémoire. Cette condition peut se produire si de nombreux objets volumineux (tels que des objets DataTable) sont déclarés puis ajoutés à une collection (comme un DataSet). Les ressources que ces objets possèdent peuvent ne jamais être libérées et les ressources sont laissées en vie pendant toute l'exécution du programme. Cela semble être une fuite, mais en fait c'est juste un symptôme de la façon dont la mémoire est allouée dans le programme.

Pour traiter ce type de problème, vous pouvez implémenter IDisposable . Si vous voulez voir certaines des stratégies pour gérer la gestion de la mémoire, je suggérerais de rechercher IDisposable, XNA, la gestion de la mémoire, car les développeurs de jeux ont besoin d'un ramassage des ordures plus prévisible et doivent donc forcer le GC à faire son travail.

Une erreur courante consiste à ne pas supprimer les gestionnaires d'événements qui s'abonnent à un objet. Un abonnement à un gestionnaire d'événements empêchera un objet d'être recyclé. Jetez également un œil à l' instruction using qui vous permet de créer une portée limitée pour la durée de vie d'une ressource.


5
Voir blogs.msdn.com/tess/archive/2006/01/23/… . Peu importe que la fuite de mémoire soit «traditionnelle» ou non, c'est toujours une fuite.
Constantin

2
Je vois votre point - mais l'allocation et la réutilisation inefficaces de la mémoire par un programme sont différentes d'une fuite de mémoire.
Timothy Lee Russell

bonne réponse, merci de me rappeler que les gestionnaires d'événements peuvent être dangereux.
frameworkninja

3
@Timothy Lee Russel: Si une quantité illimitée (1) de mémoire peut rester simultanément allouée (enracinée) après être devenue inutile (2), sans que rien dans le système n'ait les informations et l'impulsion nécessaires pour le déraciner en temps opportun, c'est une fuite de mémoire . Même si la mémoire peut être libérée un jour, si suffisamment de choses inutiles peuvent s'accumuler pour étouffer le système avant que cela n'arrive, c'est une fuite. (1) Supérieur à O (N), N étant le montant de l'allocation utile; (2) Les trucs sont inutiles si la suppression des références n'affecte pas la fonctionnalité du programme.
supercat

2
@Timothy Lee Russel: Le modèle normal de «fuite de mémoire» se produit lorsque la mémoire est détenue par une entité pour le compte d'une autre entité , s'attendant à être informée lorsqu'elle n'est plus nécessaire, mais cette dernière abandonne l'entité sans en informer la première. L'entité qui détient la mémoire n'en a pas vraiment besoin, mais il n'y a aucun moyen de le déterminer.
supercat

5

Ce blog propose de très belles procédures pas à pas utilisant windbg et d'autres outils pour détecter les fuites de mémoire de tous types. Excellente lecture pour développer vos compétences.


5

Je viens d'avoir une fuite de mémoire dans un service Windows, que j'ai corrigé.

Tout d'abord, j'ai essayé MemProfiler . Je l'ai trouvé très difficile à utiliser et pas du tout convivial.

Ensuite, j'ai utilisé JustTrace qui est plus facile à utiliser et vous donne plus de détails sur les objets qui ne sont pas supprimés correctement.

Cela m'a permis de résoudre très facilement la fuite de mémoire.


3

Si les fuites que vous observez sont dues à une implémentation de cache emballante, il s'agit d'un scénario dans lequel vous voudrez peut -être envisager l'utilisation de WeakReference. Cela pourrait aider à garantir que la mémoire est libérée si nécessaire.

Cependant, à mon humble avis, il serait préférable d'envisager une solution sur mesure - vous seul savez vraiment combien de temps vous avez besoin pour conserver les objets, donc la conception d'un code de gestion interne approprié à votre situation est généralement la meilleure approche.


3

Je préfère dotmemory de Jetbrains


vous pouvez être le seul :)
HellBaby

Je l'ai essayé aussi. Je pense que c'est un bon outil. Facile à utiliser, informatif. S'intègre à Visual Studio
redeye

Dans notre cas, lors du dépannage des fuites de mémoire, l'outil Visual Studio Snapshot s'est écrasé / n'a pas pris d'instantané. Dotmemory a gardé son calme et a géré plusieurs instantanés de plus de 3 Go avec (apparemment) facilité.
Michael Kargl

3

Big guns - Outils de débogage pour Windows

Ceci est une incroyable collection d'outils. Vous pouvez analyser les tas gérés et non gérés avec lui et vous pouvez le faire hors ligne. C'était très pratique pour déboguer l'une de nos applications ASP.NET qui continuait à recycler en raison d'une utilisation excessive de la mémoire. Je n'avais qu'à créer un vidage mémoire complet du processus vivant en cours d'exécution sur le serveur de production, toutes les analyses ont été effectuées hors ligne dans WinDbg. (Il s'est avéré que certains développeurs utilisaient excessivement le stockage de session en mémoire.)

"Si cassé c'est ..." le blog contient des articles très utiles sur le sujet.


2

La meilleure chose à garder à l'esprit est de garder une trace des références à vos objets. Il est très facile de se retrouver avec des références suspendues à des objets dont vous ne vous souciez plus. Si vous n'utilisez plus quelque chose, débarrassez-vous-en.

Habituez-vous à utiliser un fournisseur de cache avec des expirations glissantes, de sorte que si quelque chose n'est pas référencé pour une fenêtre de temps souhaitée, il soit déréférencé et nettoyé. Mais si on y accède beaucoup, cela le dira en mémoire.


2

L'un des meilleurs outils consiste à utiliser les outils de débogage pour Windows et à effectuer un vidage de la mémoire du processus à l'aide d' adplus , puis à utiliser windbg et le plugin sos pour analyser la mémoire du processus, les threads et les piles d'appels.

Vous pouvez également utiliser cette méthode pour identifier les problèmes sur les serveurs, après avoir installé les outils, partagez le répertoire, puis connectez-vous au partage à partir du serveur en utilisant (net use) et effectuez un crash ou un hang dump du processus.

Ensuite, analysez hors ligne.


Oui, cela fonctionne bien, en particulier pour des éléments plus avancés ou pour diagnostiquer des problèmes dans des logiciels publiés auxquels vous ne pouvez pas facilement attacher un débogueur. Ce blog contient de nombreux conseils pour bien utiliser ces outils: blogs.msdn.com/tess
Scott Langham

2

Après l'un de mes correctifs pour une application gérée, j'ai eu la même chose, comme comment vérifier que mon application n'aura pas la même fuite de mémoire après ma prochaine modification, j'ai donc écrit quelque chose comme le cadre de vérification de la libération des objets, veuillez jeter un œil sur le package NuGet ObjectReleaseVerification . Vous pouvez trouver un exemple ici https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample , et des informations sur cet exemple http://outcoldman.ru/en/blog/show/322


0

À partir de Visual Studio 2015, envisagez d'utiliser l' outil de diagnostic d'utilisation de la mémoire prêt à l'emploi pour collecter et analyser les données d'utilisation de la mémoire.

L'outil Utilisation de la mémoire vous permet de prendre un ou plusieurs instantanés du tas de mémoire géré et natif pour vous aider à comprendre l'impact sur l'utilisation de la mémoire des types d'objet.


0

l'un des meilleurs outils que j'ai utilisé son DotMemory.vous pouvez utiliser cet outil comme une extension dans VS.après avoir exécuté votre application, vous pouvez analyser chaque partie de la mémoire (par objet, espace de nom, etc.) que votre application utilise et en prendre un instantané , Comparez-le avec d'autres SnapShots. DotMemory

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.