1

Possible Duplicate:
Do event handlers stop garbage collection from occuring?

I had one wp7 application like this:

private void button1_Click(object sender, RoutedEventArgs e)
{
    GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();
    watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
    watcher.Start();
}

void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
    Debug.WriteLine(e.Position.Timestamp.ToString());
}

After I click the button twice,the Console will output the Timestamp twice. But the watcher was a local variable! What's wrong with it? And how can I distory it?

Community
  • 1
  • 1
Dozer
  • 5,025
  • 11
  • 36
  • 52
  • Are We talking here about this question or Something else. – joshua Jun 12 '12 at 07:34
  • @Black_Crown: He's saying that no matter what he wants to achieve, the behaviour seems strange to him and he doesn't understand why things are happening as they are. – Thorsten Dittmar Jun 12 '12 at 07:47
  • Yes,I think after the button1_Click finish,there is no instance referenced the watcher.So I think it will be collected by the GC.But it didn't. – Dozer Jun 12 '12 at 07:49
  • @ThorstenDittmar Thanks buddy...i think his problem is solved now – joshua Jun 12 '12 at 07:54
  • @ThorstenDittmar: I suspect you don't quite understand why things are happening as they are either, given your answer... – Jon Skeet Jun 12 '12 at 13:02
  • @JonSkeet: Yes, seems so :-) But still I understand *what* the problem is, though obviously I didn't quite grasp the real reason causing it. – Thorsten Dittmar Jun 12 '12 at 13:23

2 Answers2

8

watcher is a local variable, but that doesn't affect the object necessarily. You've asked the GeoCoordinateWatcher to start - I'd expect it to maintain a reference to itself, effectively, or stash one somewhere appropriate.

It sounds like either you ought to disable the button once it's clicked, or you need to keep the watcher in an instance variable so that you can dispose of the old one and create a new one. (I'm not sure why that would be useful though.)

EDIT: As there are two incorrect answers here, let me just clear something up... an event publisher (the watcher in this case) has references to the handler delegates. If those delegates refer to instance methods (as it does in this case) then there's a reference to an instance of the type containing that method:

 Event publisher => delegate => instance of type with handler method

That means that as long as the publisher isn't garbage collected (and the event handler still exists), the instance associated with the delegate can't be collected. It doesn't prevent the publisher itself from being garbage collected.

In other words, if GeoCoordinateWatcher didn't do something "special" (probably in the Start method) it could be garbage collected. There's no implicit reference from an event handler to the event publisher which prevents it from being garbage collected that way round.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • +1 for the clarification, good job at least *someone* isn't half asleep with all this! Please don't tell me you are three-quarters asleep... – Adam Houldsworth Jun 12 '12 at 13:24
  • You're right of course. I can't believe I wrote that. Maybe I _was_ half-asleep. +1 for your answer and I'll vote to delete mine. – Martin Jun 14 '12 at 07:54
-2

The GC will actually not collect watcher, as the event is still assigned (that means that the GeoCoordinateWatcher instance is still referenced and thus not collected by the GC). Even though the local variable is out of scope, the instance is still alive.

Detach the event handler in watcher_PositionChanged and things work as expected. If you don't, you have a new instance of GeoCoordinateWatcher upon every button click, and each of them will call the event when the location changes.

This is the cause for various strange problems sometimes when creating instances/assigning events locally. In cases like yours, there are two possible solutions:

  1. Create an instance variable, and make sure to do that only once
  2. Make sure to properly dispose of the old instance before creating a new one (not relying on the garbage collector)
Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • watcher contains an event.So that means watcher referenced by event? Or event referenced by watcher? – Dozer Jun 12 '12 at 07:45
  • Attaching the event handler to `watcher` increases the instance counter for the object referenced by the `watcher` variable. So even though you leave the scope of the local variable, the instance counter remains > 0 because of the attached event. – Thorsten Dittmar Jun 12 '12 at 08:31
  • 2
    You've got this the wrong way round. The *publisher* of an event keeps a reference to any *handlers*, not the other way round. (There's no "instance counter" either.) The watcher must be keeping itself alive in some other way (e.g. there may be a static list of "started" watchers somewhere). – Jon Skeet Jun 12 '12 at 12:56
  • @ThorstenDittmar GC uses a "Mark and Sweep" algorithm, no reference counting - there is a big difference – BrokenGlass Jun 12 '12 at 12:58
  • Hm. Thinking about it, you're both right. I the situations where I had similar problems I registered methods of a temporary object as event receiver and got multiple outputs because the temporary objects were not released due to the event publisher keeing a reference to the receiver... I'd delete my answer, but I'll leave it anyway as a bad example of what happens when you're distracted from SO by your day-job :-) Thanks for correcting – Thorsten Dittmar Jun 12 '12 at 13:19
  • @All Just as an aside, what would happen if the subscriber instance became eligible for GC? If something else is keeping watcher alive, and the instance is subscribed to watcher, what happens when the instance itself is disused? I would like to assume it is eligible for GC without issue, but there is a strange third-party *also* keeping watcher alive that has thrown a spanner into the works for my brain... – Adam Houldsworth Jun 12 '12 at 13:27
  • This happened in my problematic cases: The publisher kept a reference to the receiver and the receiver was not GCed. The receiver only got GCed when the publisher got GCed. – Thorsten Dittmar Jun 12 '12 at 13:31
  • @ThorstenDittmar That is the standard situation where you subscribe to a longer-lived object and do not unsubscribe. This situation differs ever so slightly in that the `watcher` instance is scoped internally to the item that subscribes to it. – Adam Houldsworth Jun 12 '12 at 13:33
  • @AdamHouldsworth: I know. You said "if something else is keeping the watcher alive" - doesn't that also make the watcher a longer-lived object as in my case? – Thorsten Dittmar Jun 12 '12 at 13:35
  • @ThorstenDittmar It does on paper, but I'm asking about clarification for whether that situation could be untangled, or if the only thing for it is to not loose references to `watcher` because you need to stop it or unsubscribe from it. – Adam Houldsworth Jun 12 '12 at 13:36
  • @AdamHouldsworth: Wait - you're talking about the OP's sample, like: the watcher is scoped to a method of the `Page`, you attach a method of the page as event handler to the watcher, what happens if the `Page` becomes eligible for GC but the watcher is kept alive by some other reference? Will the `Page` be GCed? This hurts my brain :-D – Thorsten Dittmar Jun 12 '12 at 13:43
  • 1
    @ThorstenDittmar Yes that is what I'm trying to figure out. And indeed, I just rigged up an example of a third-party holding onto something when all other references are lost and it leaks all types. So in this instance, all types are going to be leaked unless the OP unsubscribes from watcher and also stops / disposes the watcher manually. – Adam Houldsworth Jun 12 '12 at 13:43