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

StackOverflow https://stackoverflow.com/questions/134086

Question

J'ai écrit le 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 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 supprimer 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 une idée fausse 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?

Était-ce utile?

La solution

J'utilise le 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 m'a sauvé le 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.

Autres conseils

Juste pour résoudre 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

Nous avons utilisé le Ants Profiler Pro du 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 permettrait de garder les objets à portée de main simplement parce que nous pourrions l'utiliser quelque temps dans le futur. Cela signifiait que nous devions faire plus attention au nombre d'objets gonflés en mémoire. À la fin, nous avons converti tous nos objets de données en un fichier "gonfler à la demande". (juste avant qu’un champ soit demandé) 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 "détail" ou "rechercher" enregistrer sur une base individuelle nous 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 dans lequel les objets n'étaient gonflés que lorsqu'ils étaient demandés et les appels à la base de données n'étaient effectués qu'en cas de nécessité:

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’à ce qu’ils soient utilisés (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.

Vous devez toujours vous soucier de la mémoire lorsque vous écrivez du code géré, 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 tel que 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).

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.

Toutefois, la mémoire utilisée par une application ne peut 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 la Procédure d'identification des fuites de mémoire dans le Common Language Runtime de Microsoft.com

     

Une fuite de mémoire peut se produire dans un .NET   Application cadre lorsque vous utilisez   code non géré dans le cadre du   application. Ce code non géré peut   fuite de mémoire, et le .NET Framework   le moteur d'exécution ne peut pas résoudre ce problème.

     

De plus, un projet peut uniquement   semble avoir une fuite de mémoire. Ce   condition peut se produire si beaucoup de grandes   objets (tels que les objets DataTable)   sont déclarés et ensuite ajoutés à un   collection (comme un DataSet). le   les ressources que ces objets possèdent peuvent   ne jamais être libéré, et les ressources   sont laissés en vie pour toute la course de   le programme. Cela semble être un   fuite, mais en réalité c'est juste un   symptôme de la façon dont la mémoire est   être alloué 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érerais de rechercher IDisposable, XNA, la gestion de la mémoire en tant que développeurs de jeux ont besoin d'un système de récupération de place plus prévisible, ce qui oblige le GC à faire son truc.

Une erreur courante est de 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. Consultez également l’enregistrement qui vous permet de créer une étendue limitée pour la durée de vie d'une ressource.

Ce blog propose des procédures extraordinaires 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.

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 donne vous 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.

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 pourriez envisager l’utilisation de WeakReference. Cela pourrait aider à 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 saurez seulement combien de temps vous aurez besoin de conserver les objets. La conception du code d’entretien approprié à votre situation est généralement la meilleure approche.

Armes blanches - Débogage Outils pour Windows

Ceci est une collection étonnante 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 de mémoire complet du processus vivant s'exécutant 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 elle est brisée, c'est ..." le blog a été très utile articles sur le sujet.

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.

Familiarisez-vous avec l’utilisation d’un fournisseur de cache avec des expirations glissantes. Ainsi, tout élément non référencé pour une fenêtre temporelle souhaitée sera déréférencé et nettoyé. Mais si on y accède beaucoup, il le dira en mémoire.

L’un des meilleurs outils consiste à utiliser Outils de débogage pour Windows et enregistrez une image mémoire du processus à l'aide de adplus , puis utilisez windbg et le plug-in sos pour analyser la mémoire de 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 à l'aide de (utilisation du réseau) et effectuez un crash ou un blocage du processus.

Puis analysez hors connexion.

Après l’un de mes correctifs pour l’application gérée, j’ai eu la même chose. Comment vérifier que mon application n’aura pas la même fuite de mémoire après mon prochain changement; consultez le package NuGet ObjectReleaseVerification . Vous pouvez en trouver un exemple ici https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample http://outcoldman.ru/en/blog/show/322

Je préfère les dotmemory de Jetbrains

.

À partir de Visual Studio 2015, envisagez d'utiliser immédiatement les 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.

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