74 votes

Avantages et inconvénients des écouteurs en tant que références faibles

Quels sont les avantages et les inconvénients de conserver les auditeurs en tant que WeakReferences ?

Le grand "pour", bien sûr, c'est que.. :

L'ajout d'un écouteur en tant que WeakReference signifie que l'écouteur n'a pas besoin de s'effacer.

Pour ceux qui s'inquiètent du fait que l'écouteur a la seule référence à l'objet, pourquoi ne peut-il pas y avoir 2 méthodes, addListener() et addWeakRefListener() ?

Ceux qui ne se soucient pas de la suppression peuvent utiliser cette dernière.

2 votes

1 votes

Que vous apporteraient deux méthodes, à part encombrer l'api ? Bien qu'il soit (encore ?) de bon ton de supprimer tous les listeners qui ont été ajoutés si possible, il n'y a généralement pas de mal à ne pas le faire : le garbage collection s'en charge sauf dans de très rares cas. Si vous rencontrez une fuite de mémoire produite par un listener non libéré, analysez soigneusement la situation et faites quelque chose d'analogue à ce que AbstractButton fait avec son PropertyChangeListener d'Action.

1 votes

Si vous avez une écoute référencée faible, vous n'aurez pas besoin d'"analyser soigneusement la situation" ;)

75voto

BegemoT Points 2020

Tout d'abord, l'utilisation de WeakReference dans les listes d'écoute donnera à votre objet différents sémantique puis en utilisant des références dures. Dans le cas d'une référence directe, addListener(...) signifie "notifier l'objet fourni d'un ou de plusieurs événements spécifiques". jusqu'à ce que je l'arrête explicitement avec removeListener(..)", dans le cas d'une référence faible, cela signifie "notifier à l'objet fourni un ou plusieurs événements spécifiques". jusqu'à ce que cet objet ne soit plus utilisé par personne d'autre (ou arrêter explicitement avec removeListener)". Notez qu'il est parfaitement légal, dans de nombreuses situations, d'avoir un objet qui écoute certains événements et qui n'a pas d'autres références qui le maintiennent dans la GC. Le logger peut être un exemple.

Comme vous pouvez le voir, l'utilisation de WeakReference ne résout pas seulement un problème ("Je dois garder à l'esprit de ne pas oublier de supprimer l'écouteur ajouté quelque part"), mais en soulève également un autre -- "Je dois garder à l'esprit que mon écouteur peut cesser d'écouter à tout moment quand il n'y a plus de référence à lui". Vous ne résolvez pas le problème, vous échangez juste un problème contre un autre. Regardez, de quelque manière que ce soit vous êtes obligé de définir clairement, de concevoir et de tracer la durée de vie de votre auditeur, d'une manière ou d'une autre.

Donc, personnellement, je suis d'accord avec la mention que l'utilisation de WeakReference dans les listes d'écoute est plus comme un hack qu'une solution. C'est un modèle qui vaut la peine d'être connu, parfois il peut vous aider -- pour faire fonctionner correctement un code hérité, par exemple. Mais ce n'est pas un pattern de choix :)

P.S. Il faut également noter que WeakReference introduit un niveau supplémentaire d'indirection, qui, dans certains cas avec des taux d'événements extrêmement élevés, peut réduire les performances.

3 votes

Un autre problème de l'utilisation de la WeakReference - "Je dois garder à l'esprit que tout changement d'état causé par mon Listener peut encore se produire après qu'il soit sorti de la portée". Pour cette raison, un Listener WeakReference devra être conçu en sachant que son existence est non-déterministe.

0 votes

Oui, vous avez tout à fait raison. Mais il semble, d'après mon expérience, qu'il y ait de nombreux cas où ce n'est pas un problème - c'est pourquoi la question se pose :) Et vous pouvez voir des exemples d'utilisation de WeakReference pour les listeners même dans le code jdk, pour autant que je me souvienne :)

0 votes

Avez-vous des références concernant l'utilisation de WeakReference en tant qu'écouteur dans le jdk ? Je serais intéressé de voir où elle est utilisée.

32voto

Kirk Woll Points 34601

Ce n'est pas une réponse complète, mais la force même que vous citez peut aussi être sa principale faiblesse. Considérez ce qui se passerait si les écouteurs d'action étaient mis en œuvre de manière faible :

button.addActionListener(new ActionListener() {
    // blah
});

Ce listener d'action va être collecté à tout moment ! Il n'est pas rare que la seule référence à une classe anonyme soit l'événement auquel vous l'ajoutez.

1 votes

Pourquoi ne peut-il pas y avoir 2 méthodes, addListener() et addWeakRefListener() ? ceux qui ne se soucient pas de la suppression peuvent utiliser la dernière.

0 votes

@pdeva, oui, cela pourrait fonctionner. Mais c'est un peu dangereux que les gens doivent se souvenir de la méthode à appeler. Je ne pense pas que ce modèle soit nécessairement quelque chose que vous devriez toujours éviter, mais il doit avoir une bonne raison - et, selon l'OMI, cette raison ne peut pas être simplement "empêcher les erreurs des programmeurs oublieux".

2 votes

"empêcher les erreurs des programmeurs oublieux." Ce n'est pas pour ça que GC a été inventé ? :) De plus, cela simplifie votre code puisque vous n'avez pas à vous souvenir de retirer le listener lorsque vous disposez d'un objet.

12voto

JVerstry Points 12414

J'ai vu des tonnes de code où les listeners n'étaient pas désenregistrés correctement. Cela signifie qu'ils étaient toujours appelés inutilement pour effectuer des tâches inutiles.

Si une seule classe dépend d'un listener, alors il est facile de le nettoyer, mais que se passe-t-il lorsque 25 classes en dépendent ? Il devient beaucoup plus délicat de les désenregistrer correctement. Le fait est que votre code peut commencer avec un objet référençant votre listener et se retrouver dans une version ultérieure avec 25 objets référençant ce même listener.

Ne pas utiliser WeakReference équivaut à prendre le risque de consommer inutilement de la mémoire et du processeur. C'est plus compliqué, plus délicat et cela demande plus de travail avec les références dures dans le code complexe.

WeakReferences sont pleins de pros, car ils sont nettoyés automatiquement. Le seul inconvénient est que vous ne devez pas oublier de conserver une référence dure ailleurs dans votre code. Typiquement, ce serait dans les objets s'appuyant sur ce listener.

Je déteste le code créant des instances de classes anonymes d'écouteurs (comme mentionné par Kirk Woll), car une fois enregistrés, vous ne pouvez plus désenregistrer ces écouteurs. Vous n'avez pas de référence à eux. C'est vraiment un mauvais codage IMHO.

Vous pouvez également null une référence à un auditeur lorsque vous n'en avez plus besoin. Vous n'avez plus besoin de vous en préoccuper.

0 votes

Vous pouvez obtenir le tableau des écouteurs pour un composant donné. Les classes anonymes peuvent étendre une spécialisation. Donnez à cette classe d'auditeurs spécialisés une propriété uid et vous êtes prêt à supprimer les auditeurs anonymes de manière sélective.

1 votes

L'auditeur anonyme que vous n'aimez pas ne devrait être enregistré que s'il ne doit jamais être désenregistré. Si vous le sauvegardez dans une variable/un champ/une collection pour le désenregistrer plus tard, considérez-vous cela comme acceptable ?

1 votes

Si je suis d'accord avec vous pour dire que les listeners anonymes sont une arme à double tranchant, il en va de même pour l'utilisation de références faibles : Annuler une référence à l'écouteur pour le supprimer, est quelque peu antisémite. Vous devriez commenter cette ligne pour indiquer que cela signifie que l'écouteur est désenregistré à tout moment dans le futur. À mon avis, le programmeur est responsable de l'enregistrement des écouteurs, il est donc responsable de leur suppression également.

6voto

James Scriven Points 2027

Il n'y a pas vraiment d'avantages. Une référence faible est généralement utilisée pour des données "facultatives", comme un cache où vous ne voulez pas empêcher la collecte des déchets. Vous ne voulez pas que votre écouteur soit ramassé, vous voulez qu'il continue à écouter.

Mise à jour :

Ok, je pense que j'ai peut-être compris où vous voulez en venir. Si vous ajoutez des écouteurs à courte durée de vie à des objets à longue durée de vie, il peut être avantageux d'utiliser une weakReference. Ainsi, par exemple, si vous ajoutez des PropertyChangeListeners à vos objets de domaine pour mettre à jour l'état de l'interface graphique qui est constamment recréée, les objets de domaine vont s'accrocher aux interfaces graphiques, ce qui pourrait s'accumuler. Pensez à un grand dialogue popup qui est constamment recréé, avec une référence d'écouteur à un objet Employee via un PropertyChangeListener. Corrigez-moi si je me trompe, mais je ne pense pas que le modèle PropertyChangeListener soit encore très populaire.

D'autre part, si vous parlez d'écouteurs entre les éléments de l'interface graphique ou si vous avez des objets de domaine qui écoutent les éléments de l'interface graphique, vous n'achèterez rien, car lorsque l'interface graphique disparaîtra, les écouteurs disparaîtront aussi.

Voici quelques lectures intéressantes :

http://www.javalobby.org/java/forums/t19468.html

Comment résoudre les fuites de mémoire de l'écouteur swing ?

1 votes

Le type de référence approprié pour les données "facultatives" telles que la mise en cache est une SoftReference, et non une WeakReference. La WeakReference est généralement utilisée pour empêcher les fuites de références, obtenant explicitement une priorité de finalisation plus élevée que les SoftReferences de sorte qu'elles n'interfèrent pas avec le stockage des éléments en cache durant les balayages GC.

4voto

Nicolas Bousquet Points 2245

Pour être honnête, je n'adhère pas vraiment à cette idée et à ce que vous espérez faire avec un addWeakListener. Peut-être que c'est juste moi, mais cela semble être une fausse bonne idée. Au début, c'est séduisant mais les problèmes que cela pourrait impliquer ne sont pas négligeables.

Avec weakReference, vous n'êtes pas sûr que le listener ne sera plus appelé lorsque le listener lui-même ne sera plus référencé. Le ramasseur d'ordures peut libérer la mémoire quelques ms plus tard ou jamais. Cela signifie qu'il peut continuer à consommer le CPU et faire des choses étranges comme lancer une exception parce que le listener ne sera pas appelé.

Un exemple avec swing serait d'essayer de faire des choses que vous ne pouvez faire que si votre composant d'interface utilisateur est réellement attaché à une fenêtre active. Cela pourrait déclencher une exception et affecter le notificateur, le faisant planter et empêchant les auditeurs valides d'être notifiés.

Le deuxième problème, comme on l'a déjà dit, est celui des auditeurs anonymes, qui peuvent être libérés trop tôt et ne pas être notifiés du tout ou seulement quelques fois.

Ce que vous essayez de faire est dangereux car vous ne pouvez plus contrôler le moment où vous cessez de recevoir des notifications. Elles peuvent durer éternellement ou s'arrêter trop tôt.

0 votes

"Ce que vous essayez de faire est dangereux car vous ne pouvez plus contrôler quand vous arrêtez de recevoir des notifications." - CECI. Les WeakReferences permettent à un listener spécialement écrit d'être automatiquement retiré, mais c'est toujours seulement quand le GC le veut, ce qui pourrait être jamais et l'est souvent.

0 votes

J'aimerais que les gens omettent la partie "ou s'arrêter trop tôt" de cet argument - je pense que cette partie est une distraction, puisqu'il s'agit juste d'une erreur de programmation qu'il faut prendre soin d'éviter, mais ce n'est pas un problème fondamental, et lorsque vous la mentionnez, les gens peuvent avoir tendance à rejeter l'ensemble de votre argument lorsqu'ils parcourent visuellement votre réponse. La partie "peut durer indéfiniment longtemps" est le vrai problème, que vous soulignez à juste titre.

0 votes

L'arrêt trop tôt apparaît dans des cas différents mais est tout aussi problématique. S'il s'agit simplement d'un écouteur que vous espérez conserver pendant toute la durée de vie de l'application ou pendant la même durée de vie que le composant créant l'événement à écouter et que votre écouteur est collecté juste après sa création, c'est également un gros problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X