这是内存泄漏还是垃圾收集修复它

时间:2012-02-21 16:26:41

标签: c# .net performance memory-leaks garbage-collection

假设我有一个点击按钮,它会这样做:

public void ButtonClick(object sender, EventArgs e)
{
  System.Timers.Timer NewTimer = new System.Timers.Timer();
  NewTimer.AutoReset = false;
  NewTimer.Elapsed += new ElapsedEventHandler(TimerElapsed);
  NewTimer.Interval = 1000;
  NewTimer.Start(); 
}

public void TimerElapsed(object sender, ElapsedEventArgs e)
{

}

如果单击此按钮100次,那么已创建的实例会发生什么?垃圾收集是否会启动,或者System.Timers.Timer.Close方法是否需要调用,如果它在哪里,你从哪里调用它?

3 个答案:

答案 0 :(得分:5)

不,这不会导致内存泄漏。实际上,编写代码的方式并不能保证正确执行。 Timers.Timer实际上只是Threading.Timer的包装器,即使它当前正在运行,它也明确列为可收集的。

http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx

这里你没有提及它,因此下一个GC可以在你的表格仍在运行时以及在事件发生之前收集它

编辑

Timers.Timer的文档似乎不正确。如果Timer实例未被引用,则不会收集它。它确实会存在于

var timer = new System.Timers.Timer
{
    Interval = 400,
    AutoReset = true
};
timer.Elapsed += (_, __) => Console.WriteLine("Stayin alive (2)...");
timer.Enabled = true;

WeakReference weakTimer = new WeakReference(timer);
timer = null;

for (int i = 0; i < 100; i++)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

Console.WriteLine("Weak Reference: {0}", weakTimer.Target);
Console.ReadKey();

答案 1 :(得分:2)

一旦离开方法,他们将被收集。 TimerElapsed将被调用或不被调用,具体取决于Timer何时完成。很可能它会在1秒过去之前很久就死了。

当你调用Timer.Close()时,你调用Timer.Dispose()从定时器队列中取消注册定时器,在这种情况下,不会调用TimerElapsed(当然如果以前没有调用它)。

如果您未关闭计时器,GC会尽快致电Finalize(),然后拨打Dispose()。但是当它发生时,并没有确切的知识:)

见下面的示例,Console.Out.WriteLine("called!!!")永远不会执行:

using (System.Timers.Timer NewTimer = new System.Timers.Timer())
{
    NewTimer.AutoReset = false;
    ElapsedEventHandler TimerElapsed = (sender, args) => { Console.Out.WriteLine("called!!!"); };
    NewTimer.Elapsed += new ElapsedEventHandler(TimerElapsed);
    NewTimer.Interval = 1000;
    NewTimer.Start();
}

Thread.Sleep(3000);

答案 2 :(得分:2)

经过the_joric和JaredPar的回答以及运行的探查器测试显示计时器在垃圾收集之后发挥作用,因为他们留下来的原因是因为有一个对事件处理程序的引用。有关更详细的说明,请参阅this answer。

真正的答案是,除非在已用事件处理程序中关闭计时器,否则它是内存泄漏。

只是表明虽然我相信伟大的贡献者的SO(可能太多)的答案,但他们可能稍微偏离。