Vor- und Nachteile von Zuhörern als schwache Referenzen

72

Was sind die Vor- und Nachteile, wenn Hörer als schwache Referenzen gehalten werden?

Der große "Profi" ist natürlich:

Das Hinzufügen eines Listeners als WeakReference bedeutet, dass sich der Listener nicht die Mühe machen muss, sich selbst zu entfernen.

Aktualisieren

Für diejenigen, die sich Sorgen machen, dass der Listener den einzigen Verweis auf das Objekt hat, warum kann es nicht zwei Methoden geben, addListener () und addWeakRefListener ()?

diejenigen, die sich nicht für die Entfernung interessieren, können letztere verwenden.

pdeva
quelle
1
Was würden Ihnen zwei Methoden kaufen, außer die API zu überladen? Es ist zwar (immer noch?) Ein guter Stil, alle hinzugefügten Listener zu entfernen, wenn dies möglich ist. In der Regel schadet es jedoch nicht, dies nicht zu tun: Die Speicherbereinigung ist nur in sehr seltenen Fällen erforderlich. Wenn Sie auf einen Speicherverlust stoßen, der von einem nicht freigegebenen Listener verursacht wurde, analysieren Sie die Situation sorgfältig und tun Sie etwas Analoges zu dem, was AbstractButton mit dem PropertyChangeListener seiner Aktion tut
kleopatra
1
Wenn Sie einen schwach referenzierten Hörer haben, müssen Sie die Situation nicht sorgfältig analysieren;)
pdeva
Ich verwende schwache Listener, aber nicht als Teil der Schnittstelle, sondern nur intern, um die Beibehaltung eines externen Verweises auf den Listener UND die Abmeldung zu erzwingen (anon. Klassen haben diesen Verweis möglicherweise nicht). Andernfalls wird eine Referenz aufbewahrt, die nur undicht ist. Es ist wichtig zu beachten, dass auf jeden addXXXListener ein removeXXXListener folgen muss, um einen normalen Lebenszyklus sicherzustellen. Die Abmeldung ist keine Option (es sei denn, beide Objekte haben den gleichen Lebenszyklusbereich). Das Muster ist sowohl nützlich mit Swing gleichermaßen als auch in esp. für die Serverentwicklung.
Bests
1
Viele Antworten von Leuten, die sich nicht vorstellen können, warum eine WeakReference überhaupt Sinn macht. Deshalb dachte ich, ich würde zumindest einen Verweis auf das meiner Meinung nach prototypische Problem hinzufügen, das es löst: das verfallene Hörerproblem .
Don Hatch

Antworten:

73

Wenn Sie WeakReference in Listener-Listen verwenden, erhält Ihr Objekt zunächst eine andere Semantik , und dann werden harte Referenzen verwendet. Im Fall einer harten Referenz bedeutet addListener (...) "das gelieferte Objekt über bestimmte Ereignisse benachrichtigen, bis ich es explizit mit removeListener (..) stoppe", im Fall einer schwachen Referenz bedeutet dies "das gelieferte Objekt über ein bestimmtes Ereignis benachrichtigen ("). s) bis dieses Objekt von niemand anderem verwendet wird (oder explizit mit removeListener beendet wird) ". Beachten Sie, dass es in vielen Situationen vollkommen legal ist, ein Objekt zu haben, auf bestimmte Ereignisse zu warten und keine anderen Referenzen zu haben, die es von GC abhalten. Logger kann ein Beispiel sein.

Wie Sie sehen können, löst die Verwendung von WeakReference nicht nur ein Problem ("Ich sollte bedenken, dass ich nicht vergesse, irgendwo hinzugefügte Listener zu entfernen"), sondern auch ein anderes - "Ich sollte bedenken, dass mein Listener bei jedem aufhören kann, zuzuhören Moment, in dem es keinen Hinweis mehr gibt ". Sie lösen kein Problem, Sie tauschen nur ein Problem gegen ein anderes. Schauen Sie, in irgendeiner Weise haben Sie gezwungen, die Lebensspanne Ihres Zuhörers klar zu definieren, zu gestalten und zu verfolgen - auf die eine oder andere Weise.

Ich persönlich stimme der Erwähnung zu, dass die Verwendung von WeakReference in Listener-Listen eher ein Hack als eine Lösung ist. Es ist ein Muster, über das es sich zu wissen lohnt, manchmal kann es Ihnen helfen - zum Beispiel, damit Legacy-Code gut funktioniert. Aber es ist kein Muster der Wahl :)

PS Es sollte auch beachtet werden, welche WeakReference eine zusätzliche Indirektionsebene einführt, die in einigen Fällen mit extrem hohen Ereignisraten die Leistung verringern kann.

BegemoT
quelle
3
Ein weiteres Problem bei der Verwendung der WeakReference: "Ich sollte bedenken, dass Statusänderungen, die von meinem Listener verursacht werden, auch dann noch auftreten können, wenn der Bereich nicht mehr gültig ist." Aus diesem Grund muss ein WeakReference Listener mit dem Wissen entworfen werden, dass seine Existenz nicht deterministisch ist.
Bringer128
Ja, du hast definitiv recht. Aber aus meiner Erfahrung scheint es, wo es ziemlich viele Fälle gibt, in denen es kein Problem ist - warum stellt sich die Frage :) Und Sie können Beispiele für die Verwendung von WeakReference für Hörer sogar in JDK-Code sehen, soweit ich mich erinnere: )
BegemoT
Haben Sie Referenzen für die WeakReference, die als Listener im JDK verwendet wird? Es würde mich interessieren, wo es verwendet wird.
Bringer128
@ Bringer128 Nun, es scheint, ich habe mich geirrt. Ich habe viele Verwendungen von WR-basierten Hörer gesehen um Swing, aber auf der Suche Quellen , die ich keine Beispiele nicht finden können im Inneren . JDK verwendet eine interessantere Technik - die Listener-Liste selbst verwendet eine normale Referenz, aber der konkrete Listener dient als Adapter, der nur WeakRef für tatsächliche Objekte enthält, die Ereignisse empfangen - und registriert sich selbst als Ziel-GC-ed. Interessant :)
BegemoT
1
Ich kann nichts über JDK sagen, aber Android SDK verwendet eines - SharedPreference ist eines der Beispiele. Es verwendet WaekHashMap, um registrierte Listener zu speichern. grepcode.com/file/repository.grepcode.com/java/ext/… Möglicherweise sind Benennungsmethoden, die Schwachstellen ausnutzen addWeakListener(listener), eine gute Vorgehensweise, da Benutzer dieser Klasse eine Vorstellung davon haben, was im Inneren vor sich geht, um zu vermeiden, dass Benachrichtigungen fehlen
vir uns
31

Dies ist keine vollständige Antwort, aber die Stärke, die Sie zitieren, kann auch die Hauptschwäche sein. Überlegen Sie, was passieren würde, wenn Action Listener schwach implementiert würden:

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

Dieser Action-Listener wird jeden Moment Müll sammeln lassen! Es ist nicht ungewöhnlich, dass der einzige Verweis auf eine anonyme Klasse das Ereignis ist, zu dem Sie sie hinzufügen.

Kirk Woll
quelle
1
Warum kann es nicht zwei Methoden geben, addListener () und addWeakRefListener ()? diejenigen, die sich nicht für die Entfernung interessieren, können letztere verwenden
pdeva
@pdeva, ja, das würde funktionieren. Es ist allerdings etwas gefährlich, dass sich die Leute merken müssen, welche Methode sie aufrufen sollen. Ich glaube nicht , dieses Muster unbedingt etwas ist , sollten Sie immer vermeiden, aber es ist besser einen guten Grund hat - „verhindert Fehler , die von vergesslich Programmierern“ und IMO , dass die Vernunft nicht einfach sein
Kirk Woll
1
"Verhindern Sie Fehler durch vergessliche Programmierer." Ist das nicht der Grund, warum GC erfunden wurde? :) Außerdem wird Ihr Code einfacher, da Sie beim Entsorgen eines Objekts nicht daran denken müssen, den Listener zu entfernen.
Pdeva
6
@pdeva, ich meine nicht, dass es ein schlechtes Prinzip ist, es aufrechtzuerhalten. Aber ich glaube, das Medikament ist mindestens so schlimm wie das Symptom - wenn sie vergessen, das Medikament richtig anzuwenden (fügen Sie den Hörer mit der falschen Methode hinzu), sind sie in einer noch schlechteren Verfassung als die normale Lösung.
Kirk Woll
10

Ich habe Tonnen von Code gesehen, bei denen die Hörer nicht richtig abgemeldet waren. Dies bedeutet, dass sie immer noch unnötig aufgerufen wurden, um unnötige Aufgaben auszuführen.

Wenn sich nur eine Klasse auf einen Hörer verlässt, ist es leicht zu reinigen, aber was passiert, wenn 25 Klassen sich darauf verlassen? Es wird viel schwieriger, die Registrierung ordnungsgemäß aufzuheben. Tatsache ist, dass Ihr Code mit einem Objekt beginnen kann, das auf Ihren Listener verweist, und in einer zukünftigen Version mit 25 Objekten enden kann, die auf denselben Listener verweisen.

Die Nichtverwendung WeakReferenceentspricht einem hohen Risiko, unnötigen Speicher und CPU zu verbrauchen. Es ist komplizierter, kniffliger und erfordert mehr Arbeit mit harten Referenzen im komplexen Code.

WeakReferencessind voller Profis, weil sie automatisch aufgeräumt werden. Der einzige Nachteil ist, dass Sie nicht vergessen dürfen, an anderer Stelle in Ihrem Code eine feste Referenz aufzubewahren. In der Regel ist dies bei Objekten der Fall, die sich auf diesen Listener verlassen.

Ich hasse Code, der anonyme Klasseninstanzen von Listenern erstellt (wie von Kirk Woll erwähnt), weil Sie diese Listener nach der Registrierung nicht mehr abmelden können. Sie haben keinen Hinweis darauf. Es ist wirklich schlecht, IMHO zu codieren.

Sie können auch nullauf einen Listener verweisen, wenn Sie ihn nicht mehr benötigen. Sie müssen sich keine Sorgen mehr machen.

Jérôme Verstrynge
quelle
Sie können das Array von Listenern für eine bestimmte Komponente abrufen. Anonyme Klassen können eine Spezialisierung erweitern. Geben Sie dieser speziellen Listener-Klasse eine UID-Eigenschaft, und Sie können alle anon-Listener selektiv entfernen.
Alphazero
1
Der anonyme Listener, den Sie nicht mögen, sollte nur registriert werden, wenn er niemals abgemeldet werden sollte. Wenn Sie es zur späteren Abmeldung in einer Variablen / einem Feld / einer Sammlung speichern, halten Sie das dann für akzeptabel?
Bringer128
1
Ich stimme Ihnen zwar zu, dass anonyme Zuhörer ein zweischneidiges Schwert sind, aber das Gleiche gilt für mich, wenn ich schwache Referenzen verwende: Eine Referenz auf den Hörer auf Null zu setzen, um sie zu entfernen, ist etwas antisemantisch. Sie müssten diese Zeile kommentieren, dies bedeutet, dass der Listener in Zukunft jederzeit nicht mehr registriert ist. Meiner Meinung nach ist der Programmierer für die Registrierung der Hörer verantwortlich, daher ist er auch dafür verantwortlich, sie zu entfernen.
Manuel Leuenberger
@maenu Ich stimme voll und ganz zu, dass es in der Verantwortung des Programmierers liegt, Hörer zu registrieren und die Registrierung aufzuheben. Keine Diskussion darüber. Ich versuche zu argumentieren, dass es viel einfacher und weniger riskant ist, schwache Referenzen zu verwenden ...
Jérôme Verstrynge
Hier ist ein weiterer Nachteil, sich auf schwache Referenzen zu verlassen - nehmen wir an, ich habe 1000 Listener (Ansichten) für eine sich entwickelnde Simulation (Modell) und die Ansichten können sehr dynamisch kommen und gehen. Nehmen wir an, eine Benutzeraktion (z. B. die Größe eines interaktiven Fensters) kann dazu führen, dass alle Ansichten verschwinden und neu erstellt werden, beispielsweise 100 Mal. Ich habe dann 100.000 verfallene Zuhörer ... wenn der Beobachter sie als WRs festhält, werden sie schließlich von GC bereinigt ... aber bis zu diesem Zeitpunkt werden sie alle zuhören und möglicherweise teure Arbeit leisten, was zu einer erheblichen (vorübergehenden) Verlangsamung führt .
Don Hatch
5

Es gibt wirklich keine Profis. Eine schwache Referenz wird normalerweise für "optionale" Daten verwendet, z. B. für einen Cache, in dem Sie die Speicherbereinigung nicht verhindern möchten. Sie möchten nicht, dass der Müll Ihres Hörers gesammelt wird, sondern dass er weiter zuhört.

Aktualisieren:

Ok, ich glaube, ich hätte vielleicht herausgefunden, worauf du hinaus willst. Wenn Sie langlebigen Objekten kurzlebige Listener hinzufügen, kann die Verwendung einer schwachen Referenz von Vorteil sein. Wenn Sie beispielsweise PropertyChangeListeners zu Ihren Domänenobjekten hinzufügen, um den Status der GUI zu aktualisieren, die ständig neu erstellt wird, behalten die Domänenobjekte die GUIs bei, die sich aufbauen können. Stellen Sie sich einen großen Popup-Dialog vor, der ständig neu erstellt wird und dessen Listener über einen PropertyChangeListener auf ein Employee-Objekt verweist. Korrigieren Sie mich, wenn ich falsch liege, aber ich denke nicht, dass das gesamte PropertyChangeListener-Muster mehr sehr beliebt ist.

Wenn Sie dagegen über Listener zwischen GUI-Elementen sprechen oder Domänenobjekte GUI-Elemente abhören, kaufen Sie nichts, da die Listener dies auch tun, wenn die GUI nicht mehr angezeigt wird.

Hier sind ein paar interessante Lektüren:

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

Wie können Speicherlecks im Swing-Listener behoben werden?

James Scriven
quelle
1
Die richtige Referenz für 'optionale' Daten wie Caching ist eine SoftReference, keine WeakReference. WeakReference wird im Allgemeinen verwendet, um Referenzlecks zu verhindern und explizit eine höhere Finalisierungspriorität gegenüber SoftReferences zu erhalten, sodass sie die Speicherung von zwischengespeicherten Elementen während GC-Sweeps nicht beeinträchtigen.
user515655
5

Um ehrlich zu sein, kaufe ich diese Idee nicht wirklich und genau das, was Sie von einem addWeakListener erwarten. Vielleicht bin es nur ich, aber es scheint eine falsche gute Idee zu sein. Anfangs ist es verführerisch, aber die damit verbundenen Probleme sind nicht zu vernachlässigen.

Mit schwachReferenz sind Sie nicht sicher, ob der Listener nicht mehr aufgerufen wird, wenn auf den Listener selbst nicht mehr verwiesen wird. Der Garbage Collector kann einige ms später oder nie Menmory freigeben. Dies bedeutet, dass es möglicherweise weiterhin CPU verbraucht und dies seltsam macht, wie das Auslösen einer Ausnahme, da der Listener nicht aufgerufen werden soll.

Ein Beispiel für Swing wäre der Versuch, Dinge zu tun, die Sie nur tun können, wenn Ihre UI-Komponente tatsächlich an ein aktives Fenster angehängt ist. Dies kann eine Ausnahme auslösen und sich auf den Absturz des Benachrichtigers auswirken und verhindern, dass gültige Listener benachrichtigt werden.

Das zweite Problem ist, wie bereits erwähnt, der anonyme Hörer, er könnte zu früh freigegeben werden, nie oder nur ein paar Mal benachrichtigt werden.

Was Sie erreichen möchten, ist gefährlich, da Sie nicht mehr kontrollieren können, wann Sie keine Benachrichtigungen mehr erhalten. Sie können ewig dauern oder zu früh aufhören.

Nicolas Bousquet
quelle
"Was Sie erreichen möchten, ist gefährlich, da Sie nicht mehr kontrollieren können, wann Sie keine Benachrichtigungen mehr erhalten." - DIES. Mit WeakReferences kann ein speziell geschriebener Listener automatisch entfernt werden, aber es ist immer noch nur dann der Fall, wenn sich der GC verdammt gut anfühlt, was niemals und häufig der Fall sein könnte .
user515655
Ich wünschte, die Leute würden den Teil dieses Arguments "oder zu früh aufhören" weglassen - ich denke, dieser Teil ist eine Ablenkung, da dies nur ein Programmierfehler ist, der sorgfältig vermieden werden muss, aber kein grundlegendes Problem ist, und wenn Sie es erwähnen kann dazu führen, dass Leute dazu neigen, Ihr gesamtes Argument zurückzuweisen, wenn Sie Ihre Antwort visuell scannen. Der Teil "kann auf unbestimmte Zeit dauern" ist das eigentliche Problem, auf das Sie zu Recht hinweisen.
Don Hatch
Der Stopp erscheint in verschiedenen Fällen zu früh, ist aber ebenso problematisch. Wenn es sich nur um einen Listener handelt, von dem Sie hoffen, dass er für die gesamte Lebensdauer der Anwendung oder für die gleiche Lebensdauer wie die Komponente, die das zu hörende Ereignis erstellt, erhalten bleibt und Ihr Listener unmittelbar nach seiner Erstellung über Müll gesammelt wird, ist dies ebenfalls ein großes Problem.
Nicolas Bousquet
3

Da Sie den WeakReference-Listener hinzufügen, verwenden Sie vermutlich ein benutzerdefiniertes Observable-Objekt.

In der folgenden Situation ist es absolut sinnvoll, eine WeakReference für ein Objekt zu verwenden. - Das Observable-Objekt enthält eine Liste von Listenern. - Sie haben bereits einen harten Bezug zu den Zuhörern woanders. (Sie müssten sich dessen sicher sein) - Sie möchten nicht, dass der Garbage Collector die Listener nicht mehr löscht, nur weil im Observable ein Verweis darauf vorhanden ist. - Während der Speicherbereinigung werden die Listener gelöscht. In der Methode, mit der Sie die Listener benachrichtigen, löschen Sie die WeakReference-Objekte aus der Benachrichtigungsliste.

Deepak
quelle
3

Meiner Meinung nach ist es in den meisten Fällen eine gute Idee. Der Code, der für die Freigabe des Listeners verantwortlich ist, befindet sich an derselben Stelle, an der er registriert wird.

In der Praxis sehe ich eine Menge Software, die die Hörer für immer hält. Oft sind sich Programmierer nicht einmal bewusst, dass sie die Registrierung aufheben sollten.

In der Regel ist es möglich, ein benutzerdefiniertes Objekt mit einem Verweis auf den Listener zurückzugeben, mit dem manipuliert werden kann, wann die Registrierung aufgehoben werden muss. Zum Beispiel:

listeners.on("change", new Runnable() {
  public void run() {
    System.out.println("hello!");
  }
}).keepFor(someInstance).keepFor(otherInstance);

Dieser Code würde den Listener registrieren, ein Objekt zurückgeben, das den Listener kapselt und über die Methode keepFor verfügt, die den Listener zu einer statischen schwachHashMap mit dem Instanzparameter als Schlüssel hinzufügt. Dies würde garantieren, dass der Listener mindestens so lange registriert ist, wie someInstance und otherInstance nicht durch Müll gesammelt werden.

Es kann andere Methoden wie keepForever () oder keepUntilCalled (5) oder keepUntil (DateTime.now (). PlusSeconds (5)) oder unregisterNow () geben.

Die Standardeinstellung kann für immer beibehalten werden (bis sie nicht registriert ist).

Dies könnte auch ohne schwache Referenzen implementiert werden, jedoch ohne Phantomreferenzen, die das Entfernen des Listeners auslösen.

edit: hat eine kleine Bibliothek erstellt, die eine Basisversion dieses Ansatzes implementiert: https://github.com/creichlin/struwwel

Christian
quelle
Was ist, wenn die Speicherbereinigung nach dem Aufruf von, on()aber vorher erfolgt keepFor()? Vermutlich keepFor()Anrufe weakReference.get(), die möglicherweise nullschon zu diesem Zeitpunkt zurückkehren?
Glebm
@glebm, ja, du hast recht, das ist ein Problem. sehr unwahrscheinlich, aber das macht es noch schlimmer. Wenn Sie es standardmäßig für immer behalten, bis eine andere Methode aufgerufen wird, wird das Problem behoben. Oder machen Sie einfach die Referenz vom zurückgegebenen Objekt zum Listener nicht schwach. Es wird also kein Müll gesammelt, solange das zurückgegebene Objekt verwendet wird.
Christian
2

Ich kann mir keinen legitimen Anwendungsfall für die Verwendung von WeakReferences für Listener vorstellen, es sei denn, Ihr Anwendungsfall umfasst Listener, die nach dem nächsten GC-Zyklus explizit nicht mehr existieren sollten (dieser Anwendungsfall wäre natürlich VM- / plattformspezifisch).

Es ist möglich, sich einen etwas legitimeren Anwendungsfall für SoftReferences vorzustellen, bei dem die Listener optional sind, aber viel Heap beanspruchen und der erste sein sollten, der anfängt, wenn die Größe des freien Heaps anfängt, heikel zu werden. Ich nehme an, eine Art optionales Caching oder eine andere Art von assistierendem Hörer könnte ein Kandidat sein. Selbst dann scheint es so, als ob Sie möchten, dass die Interna der Listener die SoftReferences verwenden, nicht die Verbindung zwischen Listener und Listenee.

Wenn Sie jedoch ein dauerhaftes Listener-Muster verwenden, sind die Listener nicht optional. Daher kann das Stellen dieser Frage ein Symptom sein, das Sie benötigen, um Ihre Architektur zu überdenken.

Ist dies eine akademische Frage oder haben Sie eine praktische Situation, die Sie ansprechen möchten? Wenn es sich um eine praktische Situation handelt, würde ich gerne hören, was es ist - und Sie könnten wahrscheinlich mehr und weniger abstrakte Ratschläge zur Lösung erhalten.

jkraybill
quelle
Wie wäre es mit: Ein Objekt löst ein Ereignis aus, wenn etwas passiert. Sie möchten ein Objekt mit einem Feld oder einer Methode haben, die angibt, wie oft dies passiert ist. Wenn außerhalb des Ereignisses, das dieses Feld erhöht, kein Verweis auf dieses Objekt vorhanden ist, sollte das Objekt nicht mehr vorhanden sein. Wenn viele Objekte, die ein langlebiges Objekt überwachen, konstruiert und aufgegeben werden, sammeln sich die Objekte ohne Bindung weiter an, sofern das Ereignis nicht schwach abonniert ist.
Supercat
2

Ich habe 3 Vorschläge für das Originalplakat. Es tut mir leid, dass ich einen alten Thread wiederbelebt habe, aber ich denke, meine Lösungen wurden zuvor in diesem Thread nicht besprochen.

Betrachten Sie zunächst das Beispiel von javafx.beans.values.WeakChangeListener in den JavaFX-Bibliotheken.

Zweitens habe ich das JavaFX-Muster durch Ändern der addListener-Methoden meines Observable verbessert. Die neue Methode addListener () erstellt jetzt Instanzen der entsprechenden WeakXxxListener-Klassen für mich.

Die Methode "Feuerereignis" konnte leicht geändert werden, um die XxxWeakListener zu dereferenzieren und zu entfernen, wenn WeakReference.get () null zurückgab.

Die Methode zum Entfernen war jetzt etwas unangenehmer, da ich die gesamte Liste durchlaufen muss, und das bedeutet, dass ich eine Synchronisierung durchführen muss.

Drittens habe ich vor der Implementierung dieser Strategie eine andere Methode angewendet, die Sie vielleicht nützlich finden. Die (harten Referenz-) Zuhörer erhielten ein neues Ereignis, bei dem sie eine Realitätsprüfung durchführten, ob sie noch verwendet wurden oder nicht. Wenn nicht, haben sie sich vom Beobachter abgemeldet, wodurch sie GCed wurden. Für kurzlebige Zuhörer, die langlebige Observables abonniert haben, war es ziemlich einfach, Veralterung zu erkennen.

Aus Rücksicht auf die Leute, die festlegten, dass es "eine gute Programmierpraxis ist, Ihre Listener immer abzumelden, wenn ein Listener sich selbst abmeldet, habe ich einen Protokolleintrag erstellt und das Problem später in meinem Code behoben.

Teto
quelle
Dies ist eine 3 Jahre alte Frage, und Ihre "Antwort" scheint keine zu sein.
Jan Chrbolka
1
Ich habe mich ausdrücklich dafür entschuldigt, dass ich einen 3 Jahre alten Beitrag wiederbelebt habe. Aber als ich diesen Thread las, war er für mich neu und ich bin sicher, dass andere auch zum ersten Mal darüber stolpern werden, wenn sie Google "schwachen Hörer" sind. Ich denke, diese Leute werden von meinem Beitrag zur Diskussion insgesamt profitieren. Insbesondere habe ich ein spezielles Beispiel angeführt, in dem schwache Listener in einer modernen Java-API verwendet wurden (die aufgerufen wurde). Ich ging auch auf Möglichkeiten zur Minderung der "Risiken" der Verwendung schwacher Listener ein, indem ich die Verwendung der Richtlinie "Selbstentfernung erfordert einen Protokolleintrag" vorschlug.
Bis
1

WeakListener sind in Situationen nützlich, in denen GC speziell die Lebensdauer des Listeners steuern soll.

Wie bereits erwähnt, ist dies eine andere Semantik als im üblichen Fall addListener / removeListener, sie ist jedoch in einigen Szenarien gültig.

Stellen Sie sich zum Beispiel einen sehr großen Baum vor, der spärlich ist - einige Knotenebenen sind nicht explizit definiert, können aber von übergeordneten Knoten weiter oben in der Hierarchie abgeleitet werden. Die implizit definierten Knoten hören auf die übergeordneten Knoten, die definiert sind, damit sie ihren impliziten / geerbten Wert auf dem neuesten Stand halten. Aber der Baum ist riesig - wir möchten nicht, dass implizite Knoten für immer verfügbar sind -, solange sie vom aufrufenden Code verwendet werden, plus möglicherweise einem LRU-Cache von einigen Sekunden, um zu vermeiden, dass dieselben Werte immer wieder geändert werden.

Hier ermöglicht der schwache Listener es untergeordneten Knoten, Eltern zuzuhören, während ihre Lebensdauer durch Erreichbarkeit / Caching bestimmt wird, sodass die Struktur nicht alle implizierten Knoten im Speicher beibehält.

mdma
quelle
0

Möglicherweise müssen Sie Ihren Listener auch mit einer WeakReference implementieren, wenn Sie die Registrierung an einem Ort aufheben, der nicht jedes Mal aufgerufen werden kann.

Ich erinnere mich an einige Probleme mit einem unserer benutzerdefinierten PropertyChangeSupportListener, die in Zeilenansichten in unserer ListView verwendet wurden. Wir konnten keinen guten und zuverlässigen Weg finden, um die Registrierung dieser Listener aufzuheben. Daher schien die Verwendung eines WeakReference-Listeners die sauberste Lösung zu sein.

Dan J.
quelle
0

Aus einem Testprogramm geht hervor, dass anonyme ActionListener nicht verhindern, dass ein Objekt durch Müll gesammelt wird:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;

public class ListenerGC {

private static ActionListener al = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.err.println("blah blah");
        }
    };
public static void main(String[] args) throws InterruptedException {

    {
        NoisyButton sec = new NoisyButton("second");
        sec.addActionListener(al);
        new NoisyButton("first");
        //sec.removeActionListener(al);
        sec = null;
    }
    System.out.println("start collect");
    System.gc( );
    System.out.println("end collect");
    Thread.sleep(1000);
    System.out.println("end program");
}

private static class NoisyButton extends JButton {
    private static final long serialVersionUID = 1L;
    private final String name;

    public NoisyButton(String name) {
        super();
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println(name + " finalized");
        super.finalize();
    }
}
}

produziert:

start collect
end collect
first finalized
second finalized
end program
gerardw
quelle