C# - 试图找到剩余的 EventHandler 防止控制超出范围

Posted

技术标签:

【中文标题】C# - 试图找到剩余的 EventHandler 防止控制超出范围【英文标题】:C# - Trying to find remaining EventHandler preventing control from going out of scope 【发布时间】:2011-11-09 18:59:11 【问题描述】:

长话短说,我继承了一个相当复杂的应用程序,我正试图追查涉及表单的内存泄漏。现在,每次关闭表格并提出新的表格时,旧的表格都会留在记忆中。我追踪了程序中的控件拥有和设置的静态事件的问题(显然,只要设置了静态事件,即使没有其他人提到所述控件,该控件的任何实例都不会被视为超出范围)。现在,我正试图追查剩下的问题。

使用 MemProfiler 和 ANTS Memory Profile,我了解到根执行路径是这样的:

FormOpenWatch        <--       The item which remains active
System.EventHandler -- (this as Delegate)._target
System.Object[]
System.EventHandler -- (this as MultiCastDelegate)._invocationList
System.ComponentModel.EventHandlerList+ListEntry  --  handler
System.ComponentModel.EventHandlerList+ListEntry  --  next
System.ComponentModel.EventHandlerList+ListEntry  --  next
System.ComponentModel.EventHandlerList+ListEntry  --  next
System.ComponentModel.EventHandlerList+ListEntry  --  next
System.ComponentModel.EventHandlerList  --  head
PTU.MdiPTU  --  (this as Component).events        <--       The base application

有人对我可能要寻找的东西有任何见解吗?我发现基础应用程序中添加了 Shown 事件,并确保在处理表单时将其删除,但这似乎并没有解决问题。

非常感谢您提供的任何帮助。

稍后编辑:我认为我已经成功解决了好几次这个问题,但我仍然遇到问题。问题似乎源于我的 Plotter 类(和各种派生类)具有此“公共静态事件 MouseEventHandler MultiCursorMouseMove;”事件。我们有一个“光标”,它在鼠标位置显示图形的值和时间。最初,这一次只在一个图表上工作,但请求允许用户切换一种模式,在该模式下移动鼠标会在所有显示的图表上移动绘图。我编写了一个初始处理方法,在项目被实例化时将 EventHandlers 挂接到其中,我在池塘对面的伙伴重写了它以使用静态事件,该事件在构造时被分配给每个项目。他的方式更优雅,效果更好。除了它导致内存泄漏之外。使用内存分析软件表明,每次我尝试摆脱保存图的表单时,都会留下许多“带有直接 EventHandler 根的已处置实例”的案例。在这些中的每一个中,它都表明该对象要么是一个 Plotter,要么是一个由 Plotter 指向的对象。而且,在每一个中,基本链接是 MultiCursorMouseMove EventList 指向这些对象。我认为正在发生的事情是绘图仪保持活力,因为它有这个静态事件,而该静态事件又与绘图仪相关联。我已经设法通过调试器在给定点验证 MultiCursorMouseMove 是否为空,因为我的 Dispose 代码删除了每个 Plotter 的事件,但在同一点运行探查器仍会显示从 MultiCursorMouseMove 到这些类的这条链。

我目前不知道如何解决这个问题。有人吗?

【问题讨论】:

扫描 Watch 表单以订阅 outside 本身的事件。 为什么称它为静态事件? 公共静态事件 MouseEventHandler MultiCursorMouseMove; 这看起来不像是静态事件,FormOpenWatch 订阅了 MdiPTU 事件。 它位于 Plotter 控件中,该控件包含在 Panel 上,Panel 位于 FormWatchControl 上 TabControl 中的 TabPage 上。老实说,这有点拗口…… 【参考方案1】:

如果 MdiPTU 是您的应用程序的 MDI 父窗体,则听起来 FormOpenWatch 可能已订阅其事件之一。如果它没有直接这样做,您可能会在 FormOpenWatch 超类中找到订阅,或者甚至可能在可以从 MdiPTU 事件连接执行 FormOpenWatch 方法的其他代码中找到订阅。

【讨论】:

我确实发现了一个案例,其中一个父类订阅了 MdiPTU 上的 FontChanged 事件,非常兴奋,但我已经验证它已从事件列表中删除,并且仍然存在该引用给它。继续下一个想法……但感谢您指出可能是正确方向的指针。 @SeanDuggan:没关系,您对这些引用的存储方式感到困惑。当您订阅一个事件时,subscribee 维护对 subscriber 的引用。所以在你的例子中它不会有什么不同。您需要找到子窗体在哪里订阅比它更长寿的对象的事件,而不是相反。 我可能误会了。为了解释我上面所说的,FormPTU 是 FormShowDataPlot 的父类,它是 FormOpenWatch 的父类,它引用了名为 MainWindow 的 MdiPTU 实例。在 FormPTU_Shown 方法中,运行以下语句“MainWindow.FontChanged += new System.EventHandler(ChangeFont);”。哪个...现在我开始怀疑问题是否是它已被多次添加,尽管我不知道如何测试它。 附带说明,我重新安排了 Cleanup 方法以在 MainWindow 设置为 NULL 之前删除 FontChanged 事件的实例 ("MainWindow.FontChanged -= new System.EventHandler(ChangeFont);")在那里,看起来内存泄漏已经消失了,但看起来它只是从 EventHandler 的直接链接更改为间接链接(至少根据上面提到的分析工具)。 尝试在 FormOpenWatch 的 Dispose 方法中取消订阅事件。

以上是关于C# - 试图找到剩余的 EventHandler 防止控制超出范围的主要内容,如果未能解决你的问题,请参考以下文章

C#控制台基础 类型为EventHandler的事件的用法

C# 中的EventHandler实例详解-转

event & EventHandler

event & EventHandler

C# 实例解析事件委托之EventHandler

无法在 C# 中将类型“System.EventHandler”隐式转换为“System.Windows.Forms.KeyPressEventHandler”[重复]