Quelles stratégies et quels outils sont utiles pour trouver des memory leaks dans .NET?

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

Au cours des deux dernières années, j’ai écrit C #. Je trouve que j’ai toujours beaucoup de problèmes de mémoire. Ils sont difficiles à diagnostiquer et à corriger en raison de l’absence de 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 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 profilers de mémoire ne vous aident pas vraiment car il y a tellement de trucs flottants que vous ne pouvez pas trouver une 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 memory leaks dans .NET?

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 tout autre langage ou plate-forme, si vous écrivez du code incorrect, de mauvaises choses se produisent.

Juste pour le problème de l’oubli, essayez la solution décrite dans cet article . 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 

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 .NET Garbage Collector est très “sûr” dans le nettoyage des objects en mémoire (comme il se doit). Cela garderait les objects juste parce que nous pourrions les utiliser dans le futur. Cela signifiait que nous devions faire plus attention au nombre d’objects que nous gonflions en mémoire. En fin de compte, nous avons converti tous nos objects de données en un «gonflage à la demande» (juste avant qu’un champ ne soit demandé) afin de réduire la charge de mémoire et d’augmenter les performances.

EDIT: Voici une autre explication de ce que je veux dire par “gonfler à la demande”. Dans notre modèle d’object de notre firebase database, nous utilisons les propriétés d’un object parent pour exposer le ou les objects enfants. Par exemple, si nous avions un enregistrement faisant référence à un autre enregistrement “détail” ou “recherche” sur une base individuelle, nous le structurerions comme suit:

 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 véritables problèmes de mémoire et de performance lorsque de nombreux enregistrements étaient en mémoire. Nous sums donc passés à un système où les objects étaient gonflés uniquement lorsqu’ils étaient demandés, et les appels à la firebase database 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 objects étaient conservés dans la mémoire jusqu’à ce qu’ils soient nécessaires (on accédait à la méthode Get). Il a permis de réduire considérablement les performances en limitant les access à la firebase database et en générant un gain de place considérable en termes d’espace mémoire.

Vous devez toujours vous soucier de la mémoire lorsque vous écrivez du code managé, à moins que votre application ne soit sortingviale. Je vais suggérer deux choses: 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 ce qui cause votre fuite de mémoire (par exemple, vous pouvez examiner la fragmentation du tas de votre object de grande taille).

Utilisez-vous du code non géré? Si vous n’utilisez pas de code non géré, selon Microsoft, les memory leaks au sens traditionnel ne sont pas possibles.

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 memory leaks 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 géré dans le cadre de l’application. Ce code non géré peut provoquer une fuite de mémoire et le runtime .NET Framework ne peut pas résoudre ce problème.

En outre, un projet ne semble avoir une fuite de mémoire. Cette condition peut se produire si de nombreux objects volumineux (tels que des objects DataTable) sont déclarés puis ajoutés à une collection (par exemple, un DataSet). Les ressources que ces objects possèdent peuvent ne jamais être libérées et les ressources sont laissées en vie pendant toute la durée du programme. Cela semble être une fuite, mais en réalité, il s’agit simplement d’un symptôme de la façon dont la mémoire est allouée dans le programme.

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

Une erreur commune consiste à ne pas supprimer les gestionnaires d’événements abonnés à un object. Un abonnement de gestionnaire d’événement empêche un object d’être recyclé. Jetez également un coup d’oeil à l’instruction using qui vous permet de créer une scope limitée pour la durée de vie d’une ressource.

Ce blog propose de nombreuses solutions utilisant Windbg et d’autres outils pour détecter les memory leaks 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 vous donne plus de détails sur les objects qui ne sont pas disposés correctement.

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

Si les fuites que vous observez sont dues à une implémentation de la mémoire cache, vous pouvez envisager d’utiliser 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 êtes le seul à savoir combien de temps vous avez besoin pour conserver les objects.

La meilleure chose à garder à l’esprit est de garder une trace des références à vos objects. Il est très facile de se retrouver avec des références à des objects dont vous ne vous souciez plus. Si vous n’allez plus utiliser 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 soit déréférencé et nettoyé. Mais si on y accède beaucoup, cela dira en mémoire.

L’un des meilleurs outils consiste à utiliser les outils de débogage pour Windows et à prendre une image mémoire du processus à l’aide d’ adplus , puis à utiliser windbg et le plug-in sos pour parsingr la mémoire du processus, les threads et les stacks d’appels.

Vous pouvez également utiliser cette méthode pour identifier les problèmes sur les serveurs après avoir installé les outils, partager le répertoire, puis vous connecter au partage à partir du serveur en utilisant (net use) et effectuer un plantage ou bloquer le processus.

Ensuite, parsingz hors ligne.

Big guns – Outils de débogage pour Windows

Ceci est une incroyable collection d’outils. Vous pouvez parsingr à la fois les tas gérés et non gérés 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 de la surutilisation de la mémoire. Je n’avais qu’à créer un vidage complet du processus de vie sur le serveur de production, toute l’parsing était effectuée hors ligne dans WinDbg. (Il s’est avéré que certains développeurs utilisaient trop le stockage de session en mémoire.)

Le blog “If broken it is …” contient des articles très utiles sur le sujet.

Après l’un de mes correctifs pour l’application gérée, j’avais la même chose, comme vérifier que mon application n’aurait pas la même fuite de mémoire après mon prochain changement, donc j’ai écrit quelque chose comme Object Release Verification. 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

Dans Visual Studio (éditions Premium ou Enterprise), vous pouvez ouvrir le vidage du tas et déboguer le tas géré. Lorsque vous cliquez sur un type, les tabs ci-dessous vous permettent de voir quels autres types référencent ces objects.

Vous pouvez utiliser PerfView pour une parsing plus détaillée:

  1. Dans le menu Mémoire, sélectionnez Instantané du segment de mémoire.

  2. Dans la boîte de dialog qui apparaît, sélectionnez le processus à parsingr et cliquez sur Dump GC Heap. Vous pouvez éventuellement geler le processus ou forcer un GC à se produire avant de collecter le cliché.

  3. Une fois l’instantané terminé, cliquez sur Fermer.

Visual Studio et PerfView sont surtout utiles pour l’parsing agrégée. PerfView est un profileur d’échantillonnage, même lorsqu’il parsing le tas, ce qui donne parfois une image biaisée de ce à quoi ressemble le tas. Si vous devez explorer un object spécifique ou obtenir la vérité absolue sur l’ensemble de l’image, vous devez commencer à utiliser le débogueur ou le CLR MD.

Je préfère la dotmemory de Jetbrains

À partir de Visual Studio 2015, utilisez l’ outil de diagnostic Utilisation de la mémoire prêt à l’emploi pour collecter et parsingr 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 afin de mieux comprendre l’impact sur l’utilisation de la mémoire des types d’object.