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

发布于 2025-01-07 08:12:26 字数 476 浏览 1 评论 0原文

假设我有一个被单击的按钮,它会执行以下操作:

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 方法?如果需要,您从哪里调用它?

Lets say I have a button that gets clicked and it does this:

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)
{

}

If this button gets clicked 100 times what happens to those instances that have been created? Will garbage collection kick in or does the System.Timers.Timer.Close method need calling and if it does where do you call it from?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

疑心病 2025-01-14 08:12:26

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

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

在这里,您没有保留对它的引用,因此下一个 GC 可以在您的表单仍在运行时且在事件触发之前收集它

EDIT

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();

No this will not cause a memory leak. In fact the way your code is written it's not guaranteed to execute properly. Timers.Timer is really just a wrapper over Threading.Timer and it's explicitly listed as being collectable even if it's currently running.

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

Here you keep no reference to it and hence the very next GC could collect it while your form is still running and before the event ever fires

EDIT

The documentation for Timers.Timer appears to be incorrect. The Timer instance will not be collected if it's unreferenced. It will indeed live on

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();
后来的我们 2025-01-14 08:12:26

一旦方法离开,它们将被收集。 TimerElapsed 是否被调用取决于 Timer 何时最终确定。很有可能不到一秒就死掉了。

当您调用 Timer.Close() 时,您会调用 Timer.Dispose() 从计时器队列中取消注册计时器,在这种情况下,不会调用 TimerElapsed (当然,如果之前没有调用过的话)。

如果您未关闭计时器,GC 最终将调用 Finalize(),而后者又会调用 Dispose()。但没有确切的信息什么时候会发生:)

请参阅下面的示例,Console.Out.WriteLine("used!!!") 永远不会执行:

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);

They will be collected once method is left. TimerElapsed will be either called or not depending on when Timer gets finalized. Most likely it will be dead long before 1 second passed.

When you call Timer.Close() you thus call Timer.Dispose() that de-registers timer from timer queue and in that case TimerElapsed won't be called (of course if it was not called before).

If you leave timer not closed, GC will eventaully call Finalize() that in turn will call Dispose(). But there is not exact knowledge when it will happen :)

See below example, Console.Out.WriteLine("called!!!") will never execute:

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);
挖鼻大婶 2025-01-14 08:12:26

在 the_joric 和 JaredPar 的回答以及运行探查器测试之后,显示计时器在垃圾收集启动后仍然存在,其原因是因为存在对事件处理程序的引用。有关更详细的说明,请参阅 这个答案。

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

只是为了表明,尽管我相信伟大贡献者对 SO 的回答(也许太多了),但他们可能略有偏差。

After answers by the_joric and JaredPar and running profiler tests which showed timers sticking around after garbage collection kicked in the reason they stuck around was because there is a reference to the event handler sticking around. For a more detailed explanation see this answer.

The real answer is that it is a memory leak unless the timer is closed in the elapsed event handler.

Just goes to show that although I trust the answers on SO (maybe too much) from the great contributors they may be slightly off.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文