.NET事件导致无法控制的内存使用

时间:2009-09-09 13:50:34

标签: .net

假设我们有一个带有名为SimpleButton1的按钮的Windows窗体。以下代码导致无法控制的内存使用量。我做错了什么?

我的理解是,在for循环的每次迭代中,GC都会清理任何TestClass对象,并且会处理任何相关事件,因为任何事件都没有处理程序

Public Class Form1

Private Sub SimpleButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SimpleButton1.Click
    For i = 1 To 1000000
        Dim test1 As New TestClass            
    Next
End Sub
End Class

Public Class TestClass
    Private Event TestEvent(ByVal sender As Object)
End Class

请注意,我已经尝试在For循环的每次迭代结束时实现IDisposable并调用test1.Dispose(),但我怀疑我没有处理正确的资源。

*已回答:代码中没有问题,它按预期运行。问题是我在调试模式下运行它,并且创建的开销导致大量内存使用。见下面的讨论。

4 个答案:

答案 0 :(得分:5)

你没有在这里展示我怀疑的问题是,在你的TestClass中,你有事件TestEvent,而你可能正在向TestEvent添加处理程序,你可能不会删除它们。通过不删除它们,您告诉GC不要收集TestClass或处理程序的类。

答案 1 :(得分:5)

如您所示,此代码不会泄漏。垃圾收集器最终将处理您创建的类。

但是,如果你有一个Observable类和一个Observer类,Observer将调用Observable.Event += Observer.EventHandler;通过这样做,Observable现在有一个返回Observer的引用。如果你没有打电话给Observable.Event -= Observer.EventHandler,那么这个参考就会一直存在。

当您认为没有人对Observer有引用时会出现问题,但Observable的生命周期更长。尽管Observable具有对Observer的引用,但是没有代码可以知道删除引用,除非它清除所有处理程序(this.Event = null)。

这实际上是内存泄漏!

有几种方法可以解决这个问题:

  1. 在你抛弃观察者之前调用 - =。可能在Dispose()
  2. 使用Weak Events
  3. 考虑不同的事件模式,例如Event Aggregator

答案 2 :(得分:3)

  1. GC不会在每次迭代时开始动作,它会等到一个合适的时刻并整体扫除这些对象。

  2. TestEvent不应阻止GC的收集,但如果TestClass的实例在另一个对象上订阅一个事件,那么这将使它们保持活动状态。没有自动清理。

  3. 如何确定“导致无法控制的内存使用量”。测量内存使用量比听起来更难。我希望这不是基于TaskManager。

答案 3 :(得分:3)

如果您在Debug下运行,VisualStudio将创建额外的代码来帮助编辑和继续(_EncList stuff),即使您不在调试器下。

确保编译并在编译为“发布”的生产中运行。是的,这使得当它们的行为不同时很难调试内存问题,但这并不是追踪内存问题最困难的事情。