2009-03-27 5 views
9

J'apprends le C#. D'après ce que je sais, vous devez configurer les choses correctement pour que le garbage collector efface tout comme il se doit. Je suis à la recherche de la sagesse acquise au cours des années de votre part, l'intelligent. Je viens d'un arrière-plan C++ et je suis très habitué aux odeurs de code et aux modèles de développement. Je veux apprendre à quoi ressemblent les odeurs de code en C#. Donne-moi un conseil!Quelles sont les façons de résoudre les fuites de mémoire en C#

Quels sont les meilleurs moyens de supprimer les éléments?

Comment pouvez-vous comprendre quand vous avez des "fuites de mémoire"?


Edit: Je suis en train de développer un coup de poing liste de « choses à faire toujours la gestion de la mémoire »


Merci, beaucoup.

Répondre

17

C#, le .NET Framework utilise la mémoire gérée et tout (sauf les ressources non gérées allouées) est récupéré.

Il est prudent de supposer que les types gérés sont toujours collectés. Cela comprend arrays, classes et structures. N'hésitez pas à faire int[] stuff = new int[32]; et oubliez-le.

Si vous ouvrez un fichier, une connexion à une base de données ou toute autre ressource non gérée d'une classe, implémentez l'interface IDisposable et, dans votre méthode Dispose, désaffectez la ressource non managée.

Toute classe qui implémente IDisposable doit être explicitement fermée, ou utilisée dans un (je pense cool) Using block like;

using (StreamReader reader = new StreamReader("myfile.txt")) 
{ 
    ... your code here 
} 

Ici, .NET disposera du lecteur hors de la portée {}.

+1

Ce "ou" est source de confusion - même des objets IDisposable sont déchets collectés. Le GC ne sait rien sur IDisposable. Souvent, Dispose() appelle SuppressFinalize, mais cela n'a aucun rapport. –

+0

Bon point. Donc la classe contenant est ramassée, mais ses ressources sont explicitement désallouées. Je n'ai jamais pensé à ça. –

+3

"Il est prudent de supposer que les types gérés sont toujours collectés." Cette affirmation est si fausse. Rien ne peut être récupéré si l'on peut toujours l'atteindre, le programmeur doit toujours avoir en tête d'annuler ses références racines lorsqu'il n'est plus nécessaire. –

0

Quels sont les meilleurs moyens de supprimer les éléments?

REMARQUE: les travaux suivants que pour certains types contenant des ressources non gérés. Cela n'aide pas avec les types purement gérés.

Probablement la meilleure méthode consiste à implémenter et suivre le modèle IDisposable; et appelez la méthode disposer sur tous les objets l'implémentant.

La déclaration 'using' est votre meilleur ami. Autrement dit, il appellera apportez pour vous des objets implémentant IDisposable.

11

La première chose avec GC est qu'il est non-déterministe; Si vous voulez une ressource nettoyée rapidement, implémentez IDisposable et utilisez using; Cela ne collecte pas la mémoire gérée, mais peut aider beaucoup avec les ressources non managées et les chaînes suivantes.

En particulier, les choses à surveiller:

  • beaucoup d'épingler (met beaucoup de restrictions sur ce que le GC peut faire)
  • un bon nombre de finaliseurs (vous ne habituellement pas besoin d'eux; ralentit le GC)
  • événements statiques - moyen facile de garder beaucoup de grand objet graphes vivants ;-P
  • événements sur un objet longue durée de vie bon marché, qui peut voir un objet coûteux qui aurait dû être nettoyé
  • " les variables capturées "gardant accidentellement les graphiques vivants

Pour rechercher des fuites de mémoire ..." SOS "est l'une des routes les plus faciles; vous pouvez utiliser SOS pour trouver toutes les instances d'un type, et ce qui peut le voir, etc.

+1

Les événements statiques sont dangereux. – Quibblesome

3

En général, moins vous vous souciez de l'allocation de mémoire en C#, mieux vous êtes. Je laisserais à un profiler de me dire quand j'ai des problèmes avec la collection.

Vous ne pouvez pas créer de fuites de mémoire en C# de la même manière qu'en C++. Le garbage collector sera toujours "votre dos". Ce que vous pouvez faire est de créer des objets et de conserver des références même si vous ne les utilisez jamais. C'est une odeur de code à surveiller.

Autre que:

  • ont une certaine idée de la façon dont la fréquence de collecte aura lieu (pour des raisons de performance)
  • Ne pas contenir des références à des objets plus longtemps que vous avez besoin
  • Jeter des objets qui mettent en œuvre IDisposable dès que vous avez terminé avec eux (utilisez la syntaxe using)
  • appliquer correctement les IDisposable interface
0

Vous pouvez utiliser des outils comme CLR profiler il faut du temps pour apprendre à l'utiliser correctement, mais après tout, c'est gratuit. (Il m'a aidé à plusieurs reprises de trouver ma fuite de mémoire)

2

Les principales sources de fuites de mémoire que je peux penser sont:

  • références de tenue aux objets que vous ne nécessitent pas de plus (généralement dans certains sorte de collection) Donc, ici, vous devez vous rappeler que toutes les choses que vous ajoutez à une collection que vous avez aussi resteront en mémoire.

  • Avoir des références circulaires, par ex. avoir des délégués inscrits à un événement. Ainsi, même si vous ne référencez pas explicitement un objet, il ne peut pas récupérer les données collectées car l'une de ses méthodes est enregistrée en tant que délégué avec un événement. Dans ces cas, vous devez vous souvenir de supprimer le délégué avant de supprimer la référence.

  • Interaction avec le code natif et échec de la libération. Même si vous utilisez des wrappers gérés qui implémentent des finaliseurs, le CLR ne les nettoie pas assez rapidement, car il ne comprend pas l'empreinte mémoire.Vous devez utiliser le modèle using (IDisposable) {}
+1

"références circulaires" ne sont pas le vrai problème - le problème est que l'un des deux éléments * peut * être valablement vu, et l'événement maintient le deuxième objet en vie. –

1

Une autre chose à prendre en compte pour la gestion de la mémoire est si vous implémentez des modèles Observer et ne jetez pas les références correctement. Par exemple: Objet A Montres Objet B L'objet B est disposé si la référence de A à B n'est pas éliminé. Étant donné que le gestionnaire d'événements est toujours affecté au GC, il ne le considère pas comme une ressource non utilisée.

Si vous avez un petit ensemble d'objets que vous travaillez avec cela peut me hors de propos. Cependant, si vous travaillez avec des milliers d'objets, cela peut entraîner une augmentation progressive de la mémoire pendant la durée de vie de l'application.

Il y a quelques grandes applications logicielles de gestion de la mémoire pour surveiller ce qui se passe avec le tas de votre application. J'ai trouvé un grand avantage à utiliser .Net Memory Profiler.

HTH

1

Je recommande d'utiliser .NET Memory Profiler

Profiler mémoire .NET est un outil puissant pour trouver des fuites de mémoire et d'optimiser l'utilisation de la mémoire dans les programmes écrits en C#, VB.NET ou tout autre .NET La langue.

.NET Memory Profiler vous aidera à:

  • Afficher les informations de la mémoire et des ressources en temps réel
  • identifier facilement les fuites de mémoire en collectant et en comparant les instantanés de mémoire .NET
  • Trouver les instances qui sont pas correctement disposé
  • Obtenez des informations détaillées sur l'utilisation des ressources non managées
  • Optimisez l'utilisation de la mémoire
  • enquête sur les problèmes de mémoire dans le code de production
  • Perform mémoire test automatique
  • Récupérer des informations sur la mémoire native

Jetez un oeil à leurs didacticiels vidéo:

http://memprofiler.com/tutorials/

0

La meilleure façon d'assurer que les objets sont supprimés, ou dans .NET jargon, garbage-collect, est de s'assurer que toutes les références racine (références qui peuvent être tracées à travers les méthodes et les objets à la première méthode sur un la pile d'appels du thread) à un objet sont définies sur null.

Le GC ne peut pas, et ne sera pas, de recueillir un objet s'il y a des références à ce enracinées, peu importe si elle implémente IDisposable ou non.

références circulaires imposent aucune pénalité ou possibilité de fuites de mémoire, comme les marques de GC quels objets il a visité dans le graphe d'objet. Dans le cas des délégués ou des gestionnaires d'événement, il peut être courant d'oublier de supprimer la référence dans un événement à une méthode cible, de sorte que l'objet qui contient la méthode cible ne peut pas être collecté si l'événement est enraciné.

1

D'autres ont déjà mentionné l'importance d'IDisposable, et certaines des choses à surveiller dans votre code.

Je voulais suggérer des ressources supplémentaires; J'ai trouvé ce qui suit inestimable lors de l'apprentissage des détails de .NET GC et comment dépanner les problèmes de mémoire dans les applications .NET.

CLR via C# par Jeffrey Richter est un excellent livre. Vaut le prix d'achat juste pour le chapitre sur GC et la mémoire.

Ce blog (par un «Microsoft ASP.NET Escalation Engineer») est souvent ma référence pour obtenir des conseils et astuces sur l'utilisation de WinDbg, SOS et pour repérer certains types de fuites de mémoire. Tess a même conçu des démos/labs de débogage .NET qui vous guideront à travers les problèmes de mémoire courants et comment les reconnaître et les résoudre.

Debugging Tools for Windows (WinDbg, SOS, etc.)

+0

Le blog de Tess est absolument recommandé pour ce sujet, oui. –

Questions connexes