Personnellement, je n'ai pas rencontré de situation où j'ai eu besoin d'utiliser le type WeakReference dans .Net, mais la croyance populaire semble être qu'il devrait être utilisé dans les caches. Le Dr Jon Harrop a très bien argumenté contre l'utilisation de WeakReferences dans des caches dans sa réponse à cette question.
J'ai aussi souvent entendu des développeurs AS3 parler d'utiliser des références faibles pour économiser de la mémoire, mais d'après les conversations que j'ai eues, cela semble ajouter à la complexité sans nécessairement atteindre l'objectif visé, et le comportement à l'exécution est plutôt imprévisible. À tel point que beaucoup l'abandonnent simplement et gèrent l'utilisation de la mémoire avec plus de soin / optimisent leur code pour qu'il consomme moins de mémoire (ou fasse le compromis entre plus de cycles de calcul et une empreinte mémoire réduite).
Le Dr Jon Harrop a également souligné dans sa réponse que les références faibles .Net ne sont pas légères, et il existe une collection agressive de références faibles à gen0. Selon MSDN , les références longues et faibles vous permettent de recréer un objet but the state of the object remains unpredictable.
!
Compte tenu de ces caractéristiques, je ne peux pas penser à une situation où des références faibles seraient utiles. Peut-être que quelqu'un pourrait m'éclairer?
la source
Réponses:
J'ai trouvé des applications pratiques légitimes de références faibles dans les trois scénarios du monde réel suivants, qui m'est arrivé personnellement:
Application 1: gestionnaires d'événements
Vous êtes un entrepreneur Votre société vend un contrôle Spark Line pour WPF. Les ventes sont excellentes mais les coûts de support vous tuent. Trop de clients se plaignent de problèmes de processeur et de fuites de mémoire lorsqu'ils parcourent des écrans remplis de lignes d'étincelles. Le problème est que leur application crée de nouvelles lignes d'étincelle à mesure qu'elles apparaissent, mais la liaison de données empêche les anciennes d'être récupérées. Que faire?
Introduisez une référence faible entre la liaison de données et votre contrôle afin que la liaison de données à elle seule n'empêche plus votre contrôle d'être nettoyé. Ajoutez ensuite un finaliseur à votre contrôle qui arrête la liaison de données lorsqu’elle est collectée.
Application 2: graphes mutables
Vous êtes le prochain John Carmack. Vous avez inventé une nouvelle représentation graphique de surfaces de subdivisions hiérarchiques basée sur des graphes, qui donne aux jeux de Tim Sweeney des allures de Nintendo Wii. Évidemment, je ne vais pas vous dire exactement comment cela fonctionne, mais tout est centré sur ce graphe modifiable où les voisins d’un sommet peuvent être trouvés dans un fichier
Dictionary<Vertex, SortedSet<Vertex>>
. La topologie du graphique ne cesse de changer au fur et à mesure que le joueur tourne. Il n’ya qu’un problème: votre structure de données est en train de perdre des sous-graphes inaccessibles pendant son exécution et vous devez les supprimer sinon vous perdrez de la mémoire. Heureusement, vous êtes un génie. Vous savez donc qu'il existe une classe d'algorithmes spécialement conçus pour localiser et collecter des sous-graphiques inaccessibles: les éboueurs! Vous lisez l'excellente monographie de Richard Jones sur le sujetmais cela vous laisse perplexe et préoccupé par votre échéance imminente. Que faire?En remplaçant simplement votre
Dictionary
table par une table de hachage faible, vous pouvez superposer le GC existant et le faire collecter automatiquement les sous-graphiques inaccessibles pour vous! Retour à feuilleter des publicités Ferrari.Application 3: Décorer des arbres
Vous êtes suspendu au plafond d'une pièce cyclindrique à un clavier. Vous avez 60 secondes pour passer au crible certaines données BIG avant que quelqu'un ne vous trouve. Vous vous êtes préparé avec un bel analyseur basé sur un flux qui s'appuie sur le GC pour collecter des fragments d'AST après leur analyse. Mais vous réalisez que vous avez besoin de métadonnées supplémentaires sur chaque AST
Node
et que vous en avez besoin rapidement. Que faire?Vous pouvez utiliser a
Dictionary<Node, Metadata>
pour associer des métadonnées à chaque nœud, mais, à moins que vous ne l'effaciez, les références fortes du dictionnaire aux anciens nœuds AST les maintiendront en vie et entraîneront une fuite de mémoire. La solution est une table de hachage faible qui ne conserve que les références faibles aux clés et garbage collecte les liaisons clé-valeur lorsque la clé devient inaccessible. Ensuite, lorsque les noeuds AST deviennent inaccessibles, ils sont collectés et leur liaison clé-valeur est supprimée du dictionnaire, ce qui rend les métadonnées correspondantes inaccessibles. Ensuite, tout ce que vous avez à faire après la fin de votre boucle principale est de glisser vers le haut dans l’aération en vous rappelant de la remplacer au moment même où le gardien de sécurité arrive.Notez que dans les trois applications du monde réel qui m’étaient arrivées, je voulais que le GC collecte le plus agressivement possible. C'est pourquoi ce sont des applications légitimes. Tout le monde a tort.
la source
ConditionalWeakTable
en .NET).ConditionalWeakTable
c’est ce que les applications 2 et 3 utiliseraient, alors que certaines personnes d’autres postes l’utilisentDictionary<WeakReference, T>
. Vous ne savez pas pourquoi, vous vous retrouverez toujours avec une tonne deWeakReference
valeurs nuls avec des valeurs auxquelles aucune clé ne peut accéder, quelle que soit la méthode utilisée . Ridik.Microsoft document Modèles d'événements faibles .
la source
Permettez-moi de commencer par ceci et d'y revenir:
Commençons donc depuis le début:
... convient à l'avance pour toute infraction non intentionnelle, mais je vais revenir au niveau "Dick et Jane" pour un moment, car on ne peut jamais dire à son public.
Ainsi, lorsque vous avez un objet
X
- spécifions-le comme une instance declass Foo
- il NE PEUT PAS vivre seul (généralement vrai); De la même manière que "Aucun homme n'est une île", un objet ne peut être promu en îlot que de quelques manières - bien que cela s'appelle être une racine GC dans le langage CLR. Le fait d'être une racine du GC ou d'avoir une chaîne établie de connexions / références à une racine du GC est essentiellement ce qui détermine si lesFoo x = new Foo()
déchets sont collectés ou non .Si vous ne pouvez pas retrouver votre chemin vers une racine GC, soit en marchant en tas, soit en pile, vous êtes effectivement orphelin et serez probablement marqué / collecté au prochain cycle.
À ce stade, examinons quelques exemples horribles:
Tout d'abord, notre
Foo
:Assez simple - il n'est pas thread-safe, donc n'essayez pas cela, mais conserve un "décompte de références" approximatif d'instances actives et diminue lorsqu'elles sont finalisées.
Maintenant, regardons un
FooConsumer
:Nous avons donc un objet qui est déjà une racine GC propre (enfin ... pour être précis, il sera enraciné via une chaîne directement dans le domaine d'applications exécutant cette application, mais c'est un autre sujet) qui a deux méthodes. de verrouiller sur une
Foo
instance - testons-le:Maintenant, à partir de ce qui précède, vous attendez-vous à ce que l'objet-qui-était-une-fois-mentionné-par-
f
soit soit "collectionable"?Non, car il existe maintenant un autre objet contenant une référence, le
Dictionary
dans cetteSingleton
instance statique.Ok, essayons l'approche faible:
Maintenant, lorsque nous détruisons notre référence à
Foo
-ce qui était autrefoisf
, il n'y a plus de références "dures" à l'objet, il est donc collectable - laWeakReference
création par le faible auditeur ne l'empêchera pas.Bons cas d'utilisation:
Gestionnaires d'événements (bien que lisez ceci en premier: événements faibles en C # )
Vous avez une situation dans laquelle vous provoqueriez une "référence récursive" (l'objet A fait référence à l'objet B, qui fait référence à l'objet A, également appelé "fuite de mémoire")(edit: derp, bien sûr, il s'agit pas vrai)Vous voulez "diffuser" quelque chose à une collection d'objets, mais vous ne voulez pas être la chose qui les garde en vie; un
List<WeakReference>
peut être maintenu facilement, et même élagué en enlevant oùref.Target == null
la source
Comme des fuites logiques qui sont très difficiles à détecter alors que les utilisateurs ont tendance à remarquer que faire fonctionner votre logiciel pendant longtemps a tendance à prendre de plus en plus de mémoire et à devenir de plus en plus lent jusqu'à ce qu'ils redémarrent? Je ne.
Considérez ce qui se passe si, lorsque l'utilisateur demande à supprimer la ressource d'application ci-dessus,
Thing2
ne parvient pas à gérer correctement un tel événement sous:... et en vertu de laquelle l'une de ces erreurs serait vraisemblablement détectée lors des tests, et laquelle ne volerait pas sous le radar comme un insecte de chasse furtif. La propriété partagée est, plus souvent que la plupart, une idée absurde.
la source
Le ConditionalWeakTable est un exemple très illustratif de références faibles utilisées à bon escient . Il est utilisé par le DLR (entre autres endroits) pour attacher des "membres" supplémentaires à des objets.
Vous ne voulez pas que la table garde l'objet en vie. Ce concept ne pourrait tout simplement pas fonctionner sans de faibles références.
Mais il me semble que toutes les utilisations des références faibles sont venues longtemps après leur ajout au langage, car les références faibles font partie de .NET depuis la version 1.1. Il semble que vous souhaitiez simplement ajouter quelque chose, de sorte que le manque de destruction déterministe ne vous empêche pas de tourner la page du point de vue des fonctionnalités linguistiques.
la source
WeakReference
type, car la situation est beaucoup plus complexe. Il utilise différentes fonctionnalités exposées par le CLR.Si la couche de cache est implémentée avec C #, il est préférable de placer vos données dans le cache sous forme de références faibles, cela pourrait aider à améliorer les performances de votre couche de cache.
Pensez que cette approche pourrait également être appliquée à la mise en œuvre de session. Comme la session est un objet vivant longtemps la plupart du temps, il peut arriver que vous n'ayez pas de mémoire pour le nouvel utilisateur. Dans ce cas, il sera beaucoup mieux de supprimer un autre objet de session utilisateur que de lancer OutOfMemoryException.
De plus, si vous avez un objet volumineux dans votre application (une grande table de recherche, etc.), vous devriez l'utiliser très rarement et la recréation d'un tel objet n'est pas une procédure très coûteuse. Mieux vaut alors avoir comme moyen de référence la semaine de pouvoir libérer votre mémoire lorsque vous en avez vraiment besoin.
la source