事件处理程序与内存泄漏

6
我分析了一个VB.NET项目,发现有一些对象(子MDI窗体)已被处理,但未被GC移除。

MemoryProfiler分析结果如下:

"此实例已被处理,但仍由EventHandler间接引用。这通常表示EventHandler未被正确地移除,是内存泄漏的常见原因。以下实例直接由EventHandler引用。请调查它们以获取更多关于此问题的信息..."

现在,我试图弄清楚这意味着什么以及如何修复它。

我有一个MDI表单和一个子表单。经过打开/关闭操作后,子表单没有被GC收集,显然是因为仍然(间接?)被MDIForm EventHandlerList引用...

这可能是什么问题,我该如何解决?

我尝试了此线程中建议的修复方法,因为在PropertyStore中存在MDI引用问题,现在已经消除了,但出现了MDI EventHandlerList引用子表单的问题...

经过一些代码分析,我观察到了一些

AddHandler newMenu.Click, AddressOf ClickMenu

没有使用 RemoveHandler newMenu.Click, AddressOf ClickMenu 前缀。这可能是主要原因吗?

另外,关于 Handles,请问呢?

Private Sub ClickMenu(sender as Object, e as EventArgs) Handles newMenu.Click

更好的是
RemoveHandler newMenu.Click, AddressOf ClickMenu
AddHandler newMenu.Click, AddressOf ClickMenu

从内存分配的角度来看?


一段代码片段可能会有所帮助。特别是添加和删除事件处理程序以及释放代码。 - C. Ross
调用GC.Collect()会在dispose之后有任何区别吗?仅仅因为某个对象被dispose并不意味着它立即被垃圾回收。 - Ian
此外...这真的是个问题吗?阅读您链接到的论坛表明,这不会随着时间的推移而变得更糟。通过 MS 代码泄漏几 KB 会产生如此大的影响吗? - Ian
这是一个问题。实际上,每次我打开一个子窗体时,内存会增加大约1MB。 - serhio
2个回答

4
EventHandlerList被用于提供大量事件的类,以更高效地处理内存,只有在需要时才分配事件后备字段的空间,而不是在声明事件时。因此,所有主窗体的事件都存储在那里。
我希望子窗体能够监视其父窗体何时关闭(这是一个常见的使用情况),并且在收到该事件时没有取消订阅FormClosingFormClosed事件。但这只是一个猜测。要确认,请查看您的子窗体实现,并找到它订阅父窗体事件的所有情况,然后确保有相应的取消订阅操作。 更新
您发现未删除的菜单处理程序可能是您看到的问题的根本原因。尝试添加删除调用,看看是否解决了泄漏问题。

2

还有另一个对象通过事件处理程序引用了应由GC删除的对象。意思是:仍有其他对象订阅了已处理对象的事件。

你可以通过取消订阅事件(从事件中移除事件处理程序),解决这个问题,当你想要处理该对象时。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接