web-dev-qa-db-fra.com

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

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

Depuis deux ans, 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 de C # est que vous ne devriez pas avoir à vous soucier de telles choses lorsque vous le faites vraiment.

Un problème particulier que je constate est que je dois explicitement éliminer et nettoyer tout dans le code. Si je ne le fais pas, alors les profileurs de mémoire ne m'aident pas vraiment car il y a tellement de paillettes qui flottent que vous ne pouvez pas trouver une fuite parmi toutes les données qu'ils essaient de vous montrer. Je me demande si j'ai la 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 traiter les fuites de mémoire dans .NET?

148
Scott Langham

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. Il a sauvé mon bacon au moins une fois.

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

51
GEOCHET

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

    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
41
Jay Bazuzi

Nous avons utilisé Ants Profiler Pro par le logiciel 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 ramasse-miettes .NET est très "sûr" dans son nettoyage des objets en mémoire (comme il se doit). Cela garderait les objets tout simplement parce que nous pourrions l’utiliser un jour ou l’autre. Cela signifiait que nous devions faire plus attention au nombre d'objets gonflés en mémoire. En fin de compte, nous avons converti tous nos objets de données en un "gonflement à la demande" (juste avant la demande d'un champ) afin de réduire la surcharge de mémoire et d'augmenter les performances.

EDIT: Voici une explication supplémentaire de ce que je veux dire 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 référencait un autre enregistrement "de détail" ou "de recherche" sur une base individuelle, nous le structurerions de la manière suivante:

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 dans lequel les objets étaient gonflés uniquement lorsqu'ils étaient demandés et les appels à la base de données étaient effectués uniquement 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 en mémoire jusqu'à leur utilisation (la méthode Get a été utilisée). Il a considérablement amélioré les performances en limitant les accès à la base de données et a permis un gain considérable d’espace mémoire.

17
Mark

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: premièrement, lisez CLR via C # car cela vous aidera à comprendre la gestion de la mémoire dans .NET. Deuxièmement, apprendre à 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 coup d'œil à la fragmentation du tas d'objets volumineux).

7
Zac Gochenour

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 sont impossibles.

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

De Comment identifier les fuites de mémoire dans le Common Language Runtime de 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 géré 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 avoir uniquement une fuite de mémoire. Cette condition peut se produire si plusieurs objets volumineux (tels que des objets DataTable) sont déclarés puis ajoutés à une collection (tel qu'un DataSet). Les ressources que possèdent ces objets 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 réalité, c'est simplement 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 souhaitez voir certaines des stratégies relatives à la gestion de la mémoire, je vous suggère de chercher IDisposable, XNA, la gestion de la mémoire en tant que développeurs de jeux doivent avoir collecte des ordures plus prévisible et doit donc forcer le GC à faire son travail.

Une erreur courante consiste à ne pas supprimer les gestionnaires d'événements abonnés à un objet. Un abonnement au gestionnaire d’événements empêchera le recyclage d’un objet. Regardez aussi l'instruction sing qui vous permet de créer une portée limitée pour la durée de vie d'une ressource.

6
Timothy Lee Russell

Ce blog propose des procédures exceptionnelles utilisant windbg et d'autres outils permettant de détecter les fuites de mémoire de tous types. Excellente lecture pour développer vos compétences.

5
twk

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 disposés correctement.

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

5
billybob

Gros canons - Outils de débogage pour Windows

C'est une incroyable collection d'outils. Vous pouvez analyser des tas gérés et non gérés avec lui et vous pouvez le faire hors connexion. C'était très pratique pour déboguer 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 actif exécuté sur le serveur de production. Toutes les analyses ont été effectuées hors ligne dans WinDbg. (Il est apparu qu'un développeur utilisait de manière excessive le stockage de session en mémoire.)

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

3
Constantin

Si les fuites que vous observez sont dues à une implémentation de cache emballée, il s’agit d’un scénario dans lequel vous pouvez envisager l’utilisation de WeakReference. Cela pourrait aider à s'assurer que la mémoire est libérée lorsque cela est nécessaire.

Cependant, à mon humble avis, il serait préférable d’envisager une solution sur mesure - vous êtes le seul à savoir combien de temps vous avez besoin de conserver les objets. Par conséquent, la conception du code de maintenance approprié à votre situation est généralement la meilleure approche.

3
Chris Ballard

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 qui ne vous intéressent plus. Si vous n'utilisez plus quelque chose, éliminez-le.

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 temporelle souhaitée, il est déréférencé et nettoyé. Mais si on y accède beaucoup, il en dira en mémoire.

2
Gord

Après l’un de mes correctifs pour application gérée, j’ai eu le même problème: comment vérifier que mon application ne subira pas la même fuite de mémoire après mon prochain changement; 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

2
outcoldman

L’un des meilleurs outils consiste à utiliser Outils de débogage pour Windows et à effectuer une sauvegarde de la mémoire du processus à l’aide de adplus , puis utilisez windbg et le plugin sos pour analyser la mémoire de processus, les threads, et appeler des piles.

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 à l'aide de (Net Use) et effectuez un blocage ou un blocage du processus.

Puis analyser hors ligne.

2
Stuart McConnell

Je préfère point mémoire de Jetbrains

2
josepainumkal

À partir de Visual Studio 2015, envisagez d'utiliser immédiatement le logiciel Outil de diagnostic de l'utilisation de la mémoire 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 segment de mémoire géré et natif pour vous aider à comprendre l'impact de l'utilisation de la mémoire sur les types d'objet.

0