C# 为什么可以';GC不能收集绑定到事件的实例吗?

C# 为什么可以';GC不能收集绑定到事件的实例吗?,c#,events,garbage-collection,C#,Events,Garbage Collection,此问题基于此问题: 有人说:一个事件处理程序到一个实例,实例的引用计数会增加吗?为什么?正如Jon Skeet所说,你的链接问题是反向的。保持watcher活动状态的唯一方法可能是它自己的内部实现,它可以(也可以不)向负责反馈“滴答声”的较低级别对象注册事件-但这只是猜测 单击按钮两次将产生两个独立的观察者实例,每个实例都有一个订阅了位置更改事件(恰好是同一实例上的相同方法) 重要的是,方法退出后,保持观察者活动的位置改变的订阅者并不是订阅者,而是其他原因(我怀疑隐藏在地理坐标追踪者实现中)。

此问题基于此问题:


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

正如Jon Skeet所说,你的链接问题是反向的。保持
watcher
活动状态的唯一方法可能是它自己的内部实现,它可以(也可以不)向负责反馈“滴答声”的较低级别对象注册事件-但这只是猜测

单击按钮两次将产生两个独立的
观察者
实例,每个实例都有一个订阅了位置更改事件(恰好是同一实例上的相同方法)

重要的是,方法退出后,保持
观察者
活动的
位置改变的订阅者并不是
订阅者,而是其他原因(我怀疑隐藏在
地理坐标追踪者
实现中)。当方法退出时,对
watcher
的特定实例的引用将正确地从堆栈中弹出,但由于
watcher
上保留了另一个引用,CLR眼中的有效引用计数仍然大于零-因此,不符合垃圾收集的条件

因此,它将继续触发
PositionChanged
事件。由于事件中没有任何内容阻止观察者继续,我猜您可能会出现内存泄漏,因为每次单击按钮都会创建一个观察者实例并保持活动状态

您或者只需要存储和使用一个
地理坐标跟踪程序
类,或者在每次处理事件时关闭/处置/停止它


对于事件和订阅,通常考虑的是短期对象订阅长期对象

委托在类的特定实例中持有对特定方法的引用(如果它是静态方法,则只持有类型本身)。订阅事件会导致事件发布服务器通过订阅的委托无意中保留对订阅服务器实例的引用

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

如果订阅服务器是短期的,而事件发布服务器是长期的,如果您不取消订阅,则可能会发生内存泄漏。假设订阅者希望符合GC的条件,因为它对某个地方的事件有活动订阅,并且该对象仍然存在,则在将其从订阅列表中删除之前,它无法符合GC的条件。

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

您可以这样想,比如从函数开始第三部分
过程。函数的作用域消失了,但进程仍然有效


在提供的链接中,如果您两次单击按钮,这将导致
地理坐标跟踪程序的两个不同实例由相同的事件处理程序处理。因此,当您创建委托对象时,事件处理程序将从
geocordinatewatcher

的两个不同实例调用两次,它包含
方法
目标
属性。属性指向将在其上下文中调用
目标
方法的对象(第一个参数,又称

在某些情况下,对委托对象的引用将处于活动状态,从而防止对
目标
实例进行GCD。它通常发生在您有一个基于插件/插件的应用程序,或者其他类型的后期绑定情况,或者您正在做大量的代理工作,并且代理对象存储在某个集合中,或者您有一个带有代理对象的静态字段(因为从来没有收集过静态字段)等等


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

请参阅此线程,答案基本上是错误的可能会有所帮助-回答者的概念是错误的。我在答案中添加了一条注释。这完全正确,但不是OP所问的内容-引用的答案谈论的是事件处理程序使发布者保持活动状态,这是不正确的。@JonSkeet很公平,被当场抓住,没有真正阅读链接的问题。我会删除这个。划掉那个,我读了另一个问题,并修改了我的答案。@AdamHouldsworth非常感谢你!我反编译了GeoCoordinateWatcher类,发现Start()方法将自己添加到ThreadPool中,因此GC不会收集它@推土机没问题,很高兴你找到了原因。