为什么不能通过GC收集绑定到事件的实例?

时间:2012-06-12 12:52:41

标签: c# events garbage-collection

此问题基于以下问题: How to dispose the local variable that contains event

有人说:一个实例的一个事件处理程序,实例的引用计数会被添加吗?为什么呢?

3 个答案:

答案 0 :(得分:2)

正如Jon Skeet所说,你的相关问题已经倒退了。保持watcher活着的唯一事情可能是它自己的内部实现,它可能(或可能不)用较低级别的对象注册事件,该对象负责反馈" ticks"。 - 这只是猜想。

点击该按钮两次将产生两个单独的 watcher个实例,每个实例都有一个订阅者PositionChanged事件(恰好是在同一个实例上使用相同的方法。)

重要的是,并非 PositionChanged订阅者在方法退出后保持watcher活着 - 这是其他内容(我怀疑是在{ {1}}实施)。当方法退出时,对GeoCoordinateWatcher的特定实例的引用正确从堆栈中弹出,但由于另一个引用保留在{{1}上CLR眼中的有效引用计数仍然大于零 ​​- 因此, 符合垃圾回收的条件。

因此,它将继续发起watcher事件。由于事件中没有任何内容阻止观察者继续,我猜你可能会有内存泄漏,因为每次点击按钮都会创建并且保持活着一个观察者实例。

您需要存储和使用一个watcher类,或者每次处理事件时关闭/处置/停止它。

<小时/> 对事件和订阅的通常考虑是注意订阅长期对象的短期对象。

委托持有对类的特定实例中的特定方法的引用(如果它是静态方法,则仅包含类型本身)。订阅事件会导致事件发布者无意中通过订阅代理持有对订阅者实例的引用。

显然,如果将静态方法注册为事件处理程序,则由于没有实例,因此无法获得此引用计数。

如果订阅者是短暂的并且事件发布者是长期存在的,如果您没有取消订阅,则可能发生内存泄漏。假设订阅者希望有资格获得GC,因为它对某个地方的某个事件进行了有效订阅,并且对象仍然存在,在从该订阅列表中删除之前,它不能符合条件。

答案 1 :(得分:0)

通过调用GeoCoordinateWatcher.Start启动新的任务。即使它在您的代码中声明为local变量,它也会在退出函数范围后继续生效。

你可以考虑这个,就像你从函数中开始第三部分process一样。功能的范围消失了,但过程仍然存在。

在提供的链接中,如果您单击两次按钮,则会导致{strong>相同事件处理程序处理GeoCoordinateWatcher的2个不同实例。因此,事件处理程序将从GeoCoordinateWatcher的两个不同实例中调用两次。

答案 2 :(得分:0)

创建委托对象时,它包含MethodTarget属性。 Target属性指向将在其中调用Target方法的上下文的对象(第一个参数,即this)。

在某些情况下,对委托对象的引用将处于活动状态,从而阻止Target实例被GCed。它通常发生在你有一个基于插件/插件的应用程序,或者某种其他类型的后期绑定情况,或者你在委托中做了很多工作并且委托对象存储在某个集合中,或者你有一个静态字段使用委托对象,(因为永远不会收集静态字段)等。

请记住,垃圾收集不容易受到“循环引用”问题的影响;对于一个被认为“有用”的对象,它必须可以从当前堆栈中访问。