Verhindern Event-Handler das Auftreten von Garbage Collection?

183

Wenn ich folgenden Code habe:

MyClass pClass = new MyClass();
pClass.MyEvent += MyFunction;
pClass = null;

Wird pClass Müll gesammelt? Oder wird es herumhängen und immer noch seine Ereignisse abfeuern, wenn sie auftreten? Muss ich Folgendes tun, um die Speicherbereinigung zu ermöglichen?

MyClass pClass = new MyClass();
pClass.MyEvent += MyFunction;
pClass.MyEvent -= MyFunction;
pClass = null;
Mark Ingram
quelle
11
Ich werde Lesern, die an dieser Frage interessiert sind, vorläufig vorschlagen, dass es sich lohnen könnte, sich mit leichten Ereignissen / schwachen Ereignismustern vertraut zu machen, die das Auftreten von Speicherbereinigung NICHT verhindern. Ein guter SO-Bootstrap zu diesem Thema ist stackoverflow.com/questions/185931/…
fostandy
20
Hinweis für die Nachwelt: Wenn Sie die Referenz auf Null setzen, wird der Garbage Collector einfach verzögert, indem der Umfang der Referenz um eine Zeile erweitert wird. .NET ist nicht VB6.
John Saunders

Antworten:

207

Für die spezielle Frage "Wird pClass durch Müll gesammelt?": Das Ereignisabonnement hat keine Auswirkungen auf die Sammlung von pClass (als Herausgeber).

Für GC im Allgemeinen (insbesondere das Ziel): Es hängt davon ab, ob MyFunction statisch oder instanzbasiert ist.

Ein Delegat (z. B. ein Ereignisabonnement) für eine Instanzmethode enthält einen Verweis auf die Instanz. Ja, ein Event-Abonnement verhindert GC. Sobald jedoch das Objekt, das das Ereignis veröffentlicht (pClass oben), zur Erfassung berechtigt ist, ist dies kein Problem mehr.

Beachten Sie, dass dies eine Einbahnstraße ist. dh wenn wir haben:

publisher.SomeEvent += target.SomeHandler;

dann wird "Herausgeber" "Ziel" am Leben erhalten, aber "Ziel" wird "Herausgeber" nicht am Leben erhalten.

Also nein: Wenn pClass trotzdem gesammelt werden soll, müssen die Listener nicht abgemeldet werden. Wenn pClass jedoch langlebig wäre (länger als die Instanz mit MyFunction), könnte pClass diese Instanz am Leben erhalten, so dass dies der Fall wäre notwendig sein, abmelden , wenn Sie das Ziel gesammelt werden sollen.

Aus diesem Grund sind statische Ereignisse jedoch sehr gefährlich, wenn sie mit instanzbasierten Handlern verwendet werden.

Marc Gravell
quelle
6
Nun, wenn die Frage "Wird pClass Müll gesammelt" lautet, ist die Antwort "Es hängt davon ab, ob ..." nicht richtig. Es hängt von nichts ab, wie Marc selbst weiter unten bemerkt.
Tor Haugen
@ Tor - fair genug - ich werde klarstellen
Marc Gravell
Obwohl ein Delegierter eines Ereignisabonnements nur in eine Richtung weist, benötigt ein Abonnent, der beabsichtigt, sich von einem Ereignis abzumelden, wenn er damit fertig ist, eine Art Verweis auf den Herausgeber. Es könnte ein seinWeakReference eine gute Idee , und in einigen Fällen könnte dies eine gute Idee sein, aber so oft wie nicht wird es eine starke sein.
Supercat
Eine großartige Antwort, da sie auch die andere Hälfte der Frage anspricht (die nicht gestellt wurde): Der Herausgeber verhindert , dass der Abonnent GC-fähig wird.
Bob Sammers
Ja, und wie @BobSammers sagte, könnte es wirklich ein Problem sein, wenn eine Instanz mit kurzer Lebensdauer wie ein Formular / Fenster einen Dienst mit langer Lebensdauer wie einen Singleton abonniert, der beispielsweise Daten bereitstellt: Der Singleton behält dann eine Referenz und Objekte werden daher auch dann im Gedächtnis behalten, wenn wir glauben, dass sie entladen sind! Seien Sie also sehr vorsichtig, wenn Sie Ereignisse verwenden. Wir haben mit Ereignissen für unsere große Software missbraucht, und es ist sehr schwer, danach zu lösen.
Elo
9

Ja, pClass wird Müll gesammelt. Das Ereignisabonnement bedeutet nicht, dass ein Verweis auf pClass vorhanden ist.

Und so nein, Sie müssen den Handler nicht trennen, damit pClass Müll gesammelt wird.

Tor Haugen
quelle
8

Sobald ein Speicher nicht mehr referenziert wird, wird er zum Kandidaten für die Speicherbereinigung. Wenn die Instanz Ihrer Klasse den Gültigkeitsbereich verlässt, wird sie von Ihrem Programm nicht mehr referenziert. Es wird nicht mehr verwendet und kann daher sicher gesammelt werden.

Wenn Sie nicht sicher sind, ob etwas gesammelt wird, stellen Sie sich folgende Frage: Gibt es noch einen Verweis darauf? Die Ereignishandler werden von der Objektinstanz referenziert, nicht umgekehrt.

lvaneenoo
quelle
0

pClasswird Müll gesammelt. Wenn jedoch der obige Code innerhalb einer anderen Klasse Snippet ist, kann die Instanz dieser Klasse nicht gelöscht werden , wenn Sie nicht festgelegt haben , pClassum null.

Arav
quelle