Mise à jour (1er décembre 2009):
Je voudrais modifier cette réponse et admettre que la réponse originale était erronée.
L'analyse initiale ne s'applique aux objets qui nécessitent la finalisation - et le point que les pratiques ne devraient pas être acceptés à la surface sans précision, en profondeur la compréhension est encore debout.
Cependant, il s'avère que DataSets, DataViews, DataTables suppriment la finalisation dans leurs constructeurs - c'est pourquoi appeler Dispose () sur eux explicitement ne fait rien.
Vraisemblablement, cela se produit parce qu'ils n'ont pas de ressources non gérées; donc malgré le fait que MarshalByValueComponent prend en compte les ressources non gérées, ces implémentations particulières n'ont pas besoin et peuvent donc renoncer à la finalisation.
(Le fait que les auteurs de .NET prennent soin de supprimer la finalisation sur les types mêmes qui occupent normalement le plus de mémoire témoigne de l'importance de cette pratique en général pour les types finalisables.)
Néanmoins, le fait que ces détails soient encore sous-documentés depuis la création du .NET Framework (il y a près de 8 ans) est assez surprenant (que vous êtes essentiellement laissé à vos propres appareils pour passer au crible des éléments contradictoires et ambigus pour assembler les éléments est parfois frustrant mais fournit une compréhension plus complète du cadre sur lequel nous nous appuyons tous les jours).
Après beaucoup de lecture, voici ma compréhension:
Si un objet nécessite une finalisation, il pourrait occuper de la mémoire plus longtemps qu'il n'en a besoin - voici pourquoi: a) Tout type qui définit un destructeur (ou hérite d'un type qui définit un destructeur) est considéré comme finalisable; b) Lors de l'allocation (avant l'exécution du constructeur), un pointeur est placé dans la file d'attente de finalisation; c) Un objet finalisable nécessite normalement la récupération de 2 collections (au lieu de la norme 1); d) La suppression de la finalisation ne supprime pas un objet de la file d'attente de finalisation (comme indiqué par! FinalizeQueue dans SOS) Cette commande est trompeuse; Savoir quels objets se trouvent dans la file d'attente de finalisation (en soi) n'est pas utile; Il serait utile de savoir quels objets se trouvent dans la file d'attente de finalisation et nécessitent encore une finalisation (existe-t-il une commande pour cela?)
La suppression de la finalisation désactive un peu dans l'en-tête de l'objet indiquant au runtime qu'il n'a pas besoin d'avoir son Finalizer invoqué (n'a pas besoin de déplacer la file d'attente FReachable); Il reste dans la file d'attente de finalisation (et continue d'être signalé par! FinalizeQueue dans SOS)
Les classes DataTable, DataSet, DataView sont toutes enracinées dans MarshalByValueComponent, un objet finalisable qui peut (potentiellement) gérer des ressources non gérées
- Parce que DataTable, DataSet, DataView n'introduisent pas de ressources non managées, ils suppriment la finalisation dans leurs constructeurs
- Bien qu'il s'agisse d'un modèle inhabituel, cela évite à l'appelant d'avoir à se soucier d'appeler Dispose après utilisation
- Cela, et le fait que les DataTables peuvent potentiellement être partagés entre différents DataSets, est probablement la raison pour laquelle les DataSets ne se soucient pas de supprimer les DataTables enfants
- Cela signifie également que ces objets apparaîtront sous! FinalizeQueue dans SOS
- Cependant, ces objets devraient toujours être récupérables après une seule collection, comme leurs homologues non finalisables
4 (nouvelles références):
Réponse originale:
Il y a beaucoup de réponses trompeuses et généralement très pauvres à ce sujet - toute personne qui a atterri ici devrait ignorer le bruit et lire attentivement les références ci-dessous.
Sans aucun doute, Dispose doit être appelé sur tous les objets Finalizable.
Les DataTables sont finalisables.
Calling Dispose accélère considérablement la récupération de mémoire.
MarshalByValueComponent appelle GC.SuppressFinalize (this) dans son Dispose () - ignorer cela signifie avoir à attendre des dizaines sinon des centaines de collections Gen0 avant que la mémoire ne soit récupérée:
Avec cette compréhension de base de la finalisation, nous pouvons déjà déduire des choses très importantes:
Premièrement, les objets qui doivent être finalisés vivent plus longtemps que les autres. En fait, ils peuvent vivre beaucoup plus longtemps. Par exemple, supposons qu'un objet qui se trouve dans gen2 doit être finalisé. La finalisation sera planifiée mais l'objet est toujours dans gen2, il ne sera donc pas recollecté avant la prochaine collecte de gen2. Cela pourrait être très long en effet, et, en fait, si les choses vont bien, ce sera long, car les collections gen2 sont coûteuses et nous voulons donc qu'elles se produisent très rarement. Les objets plus anciens nécessitant une finalisation peuvent devoir attendre des dizaines sinon des centaines de collections gen0 avant de récupérer leur espace.
Deuxièmement, les objets qui doivent être finalisés provoquent des dommages collatéraux. Étant donné que les pointeurs d'objet internes doivent rester valides, non seulement les objets directement à finaliser resteront en mémoire, mais tout ce à quoi l'objet fait référence, directement et indirectement, restera également en mémoire. Si un énorme arbre d'objets était ancré par un seul objet qui nécessitait la finalisation, alors l'arbre entier resterait, potentiellement pendant longtemps comme nous venons de le discuter. Il est donc important d'utiliser les finaliseurs avec parcimonie et de les placer sur des objets qui ont le moins de pointeurs d'objets internes possible. Dans l'exemple d'arbre que je viens de donner, vous pouvez facilement éviter le problème en déplaçant les ressources qui ont besoin de finalisation vers un objet séparé et en gardant une référence à cet objet à la racine de l'arbre.
Enfin, les objets nécessitant une finalisation créent du travail pour le thread de finalisation. Si votre processus de finalisation est complexe, le seul et unique thread de finaliseur passera beaucoup de temps à effectuer ces étapes, ce qui peut entraîner un retard de travail et, par conséquent, la persistance de plus d'objets en attente de finalisation. Par conséquent, il est extrêmement important que les finaliseurs fassent le moins de travail possible. Rappelez-vous également que bien que tous les pointeurs d'objet restent valides pendant la finalisation, il se peut que ces pointeurs conduisent à des objets qui ont déjà été finalisés et peuvent donc être moins qu'utiles. Il est généralement plus sûr d'éviter de suivre les pointeurs d'objet dans le code de finalisation même si les pointeurs sont valides. Un chemin de code de finalisation sûr et court est le meilleur.
Prenez-le de quelqu'un qui a vu des centaines de Mo de DataTables non référencés dans Gen2: cela est extrêmement important et complètement ignoré par les réponses sur ce fil.
Références:
1 -
http://msdn.microsoft.com/en-us/library/ms973837.aspx
2 -
http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry
http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/