扩展方法中的 Lambda:可能存在内存泄漏吗?

发布于 2024-08-27 10:40:00 字数 2069 浏览 8 评论 0原文

我刚刚回答了一个非常简单的问题通过使用扩展方法。但写下来后我记得你不能从事件处理程序中取消订阅 lambda。

到目前为止还没有什么大问题。但是这一切在扩展方法中是如何表现的呢?

下面是我再次截取的代码。那么谁能启发我,如果多次调用此扩展方法,这是否会导致无数的计时器在内存中徘徊?

我会说不,因为计时器的范围仅限于这个函数。因此,在离开它之后,没有其他人可以引用该对象。我只是有点不确定,因为我们在静态类中的静态函数中。

public static class LabelExtensions
{
    public static Label BlinkText(this Label label, int duration)
    {
        System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

        timer.Interval = duration;
        timer.Tick += (sender, e) =>
            {
                timer.Stop();
                label.Font = new Font(label.Font, label.Font.Style ^ FontStyle.Bold);
            };

        label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold);
        timer.Start();

        return label;
    }
}

更新

只是为了澄清我使用了 System.Windows.Forms。计时器。因此,从您的答案看来,特别是使用这个计时器类是正确的选择,因为它按照我在这种情况下所期望的方式执行任何操作。如果您在这种情况下尝试另一个计时器类,您可能会遇到麻烦 马修发现了。 我还通过使用 找到了一种方法 ="http://msdn.microsoft.com/en-us/library/system.weakreference.aspx" rel="nofollow noreferrer">WeakReference 判断我的对象是否存活。

更新2

经过一点睡眠和更多思考后,我还对我的测试器做了另一个更改(答案如下)我刚刚在最后一行后添加了一个GC.Collect()并将持续时间设置为10000。启动BlinkText()后 有好几次我一直按着我的button2来获取当前状态并强制进行垃圾收集。看起来所有计时器都会在调用 Stop() 方法之后被销毁。因此,当我的 BlinkText 已经离开并且计时器正在运行时,垃圾收集也不会导致任何问题。

因此,在您做出了良好的反应并进行了更多测试之后,我可以高兴地说,它只是做了它应该做的事情,不会将计时器留在内存中,也不会在计时器完成工作之前丢弃它们。

I just gave an answer to a quite simple question by using an extension method. But after writing it down i remembered that you can't unsubscribe a lambda from an event handler.

So far no big problem. But how does all this behave within an extension method??

Below is my code snipped again. So can anyone enlighten me, if this will lead to myriads of timers hanging around in memory if you call this extension method multiple times?

I would say no, cause the scope of the timer is limited within this function. So after leaving it no one else has a reference to this object. I'm just a little unsure, cause we're here within a static function in a static class.

public static class LabelExtensions
{
    public static Label BlinkText(this Label label, int duration)
    {
        System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

        timer.Interval = duration;
        timer.Tick += (sender, e) =>
            {
                timer.Stop();
                label.Font = new Font(label.Font, label.Font.Style ^ FontStyle.Bold);
            };

        label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold);
        timer.Start();

        return label;
    }
}

Update

Just to clarify i used System.Windows.Forms.Timer. So from your answers it seems, that especially using this timer class just was the right choice cause it does anything the way as i would expect it in this case. If you try another timer class in this case you can run into trouble as Matthew found out.
Also i found a way by using WeakReferences if my objects are staying alive or not.

Update 2

After a little sleep and a little more thinking, i also made another change to my tester (answer below) i just added a GC.Collect() after the last line and set the duration to 10000. After starting the BlinkText() several times i hitted all the time my button2 to get the current state and to force a garbage collection. And as it seems all the timers will be destroyed after calling the Stop() method. So also a garbage collection while my BlinkText is already left and the timer is running doesn't lead to any problems.

So after all your good responses and a little more testing i can happily say that it just does what it should without leaving timers in the memory nor throwing away the timers before they done their work.

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

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

发布评论

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

评论(7

演多会厌 2024-09-03 10:40:00

你的假设是正确的。 timer 将在调用该方法时分配,并在最后有资格进行垃圾回收。

amba 事件处理程序(假设它没有在其他地方引用)也将有资格进行垃圾收集。

这是静态方法和/或扩展方法这一事实并没有改变对象可达性的基本规则。

Your assumption is correct. timer will be allocated when the method is called and be eligible for garbage collection at the end.

The lamba event handler (assuming it's not referenced elsewhere) will also be eligible for garbage collection.

The fact that this is a static method and/or an extension method doesn't change the basic rules of object reachability.

小…楫夜泊 2024-09-03 10:40:00

您的代码是正确的,不会泄漏内存或资源,但因为您在事件处理程序中停止了计时器。如果您注释掉 //timer.Stop();,即使您稍后执行 GC.Collect();,它也会继续闪烁。

定时器分配一个窗口来监听 WM_ 消息,并以某种方式锚定它。当计时器停止时 (Enabled = false),它会释放该窗口。
静态扩展方法上下文不起作用。

Your code is correct and will not leak memory or resources but only because you stop the Timer in the eventhandler. If you comment out the //timer.Stop(); it will keep on blinking, even when you do a GC.Collect(); later on.

The Timer allocates a Window to listen for WM_ messages and is somehow anchored by that. When the Timer is stopped (Enabled = false), it releases that Window.
The static or extension method context does not play a role.

北风几吹夏 2024-09-03 10:40:00

内存泄漏不是你的问题。垃圾收集器可以处理计时器对象。但是,虽然计时器没有停止,但似乎存在对计时器的引用,因此不会过早地处理它。要检查这一点,请从 Timer 派生类 MyTimer,重写 Dispose(bool),并设置断点。 BlinkText 之后调用 GC.Collect 和 GC.WaitForPendingFinalizers。第二次调用后,第一个计时器实例被释放。

Memory leaks are not your problem here. The garbage collector can dispose the timer object. But while the timer is not stopped, there seems to be a reference to the timer, so it is not disposed prematurely. To check this, derive a class MyTimer from Timer, override Dispose(bool), and set a breakpoint. After BlinkText call GC.Collect and GC.WaitForPendingFinalizers. After the second call, the first timer instance is disposed.

悍妇囚夫 2024-09-03 10:40:00

我刚刚做了一个有趣的测试 @cornerback84< /a> 说。我创建了一个带有标签和两个按钮的表单。一个按钮绑定您的扩展方法,另一个按钮绑定强制 GC.Collect()。然后我删除了事件循环中的 timer.Stop() 。点击开始按钮好几次真的很有趣。眨眼的间隔非常混乱。

看起来这里确实存在内存/资源泄漏......但话又说回来,计时器是一个永远不会被处置的一次性对象。

编辑:...

让乐趣开始...这也可能取决于您使用的计时器

  • System.Windows.Forms.Timer =>;这
    不会收集但会工作
    系统消息泵很好。如果您调用.Stop(),此对象可能会符合收集条件。

  • System.Timers.Timer =>;这不
    使用消息泵。青春也多
    调用消息泵。这不会
    收集。如果您调用.Stop(),则该对象可能符合收集条件。

  • System.Threading.Timer =>;这需要调用消息泵。但这也会在 GC.Collect() 上停止(这个版本似乎没有泄漏)

I just did a fun test along the lines of what @cornerback84 was saying. I created a form with a label and two buttons. One button bound your extension method, the other bound a forced GC.Collect(). I then removed the timer.Stop() in your event loop. It was really fun to click on the start button several times. The interval of the blink got pretty mixed up.

It does look like there is a Memory/Resource leak here... but then again Timer is a disposable object that is never disposed.

Edit:...

Let the fun begin... this may also depend on the Timer that you use.

  • System.Windows.Forms.Timer => this
    will not collect but will work just
    fine with the system message pump. If you call .Stop() this object could become eligible to collect.

  • System.Timers.Timer => this does not
    use the message pump. Yout much also
    call the message pump. This will not
    collect. If you call .Stop() this object could become eligible to collect.

  • System.Threading.Timer => This requires invokeing the message pump. But this will also stop on a GC.Collect() (this version didn't seem to leak)

感受沵的脚步 2024-09-03 10:40:00

看起来计时器将一直保持活动状态直到程序退出。我尝试处理标签并将其引用设置为空,但直到退出程序才收集计时器。

It seems like that Timer will stay alive until the program exit. I tried to dispose the Label and setting its reference to null, but the Timer was not collected until I exited the program.

热血少△年 2024-09-03 10:40:00

阅读完您的所有答案后,我找到了一种更好地测试它的方法。

我按以下方式更新了我的扩展类:

public static class LabelExtensions
{
    public static List<WeakReference> _References = new List<WeakReference>();

    public static Label BlinkText(this Label label, int duration)
    {
        Timer timer = new Timer();

        _References.Add(new WeakReference(timer));

        timer.Interval = duration;
        timer.Tick += (sender, e) =>
        {
            timer.Stop();
            label.Font = new Font(label.Font, label.Font.Style ^ FontStyle.Bold);
        };

        label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold);
        timer.Start();

        return label;
    }
}

此外,我还创建了第二个按钮、第二个标签,并将以下代码添加到单击事件中:

    private void button2_Click(object sender, EventArgs e)
    {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < LabelExtensions._References.Count; i++)
        {
            var wr = LabelExtensions._References[i];
            sb.AppendLine(i + " alive: " + wr.IsAlive);
        }

        label2.Text = sb.ToString();
    }

现在,我只是多次按下第一个按钮,让第一个标签闪烁。然后我按下第二个按钮并得到一个列表,我可以在其中查看我的计时器是否仍然有效。第一次点击时,他们都得到了正确的结果。但后来我再次点击第一个按钮几次,当我更新状态消息时,我看到第一个项目在 IsAlive 中已经出现错误。所以我可以有把握地说这个函数不会导致任何内存问题。

After reading all your answers i just found a way to better test it.

I updated my Extension class the following way:

public static class LabelExtensions
{
    public static List<WeakReference> _References = new List<WeakReference>();

    public static Label BlinkText(this Label label, int duration)
    {
        Timer timer = new Timer();

        _References.Add(new WeakReference(timer));

        timer.Interval = duration;
        timer.Tick += (sender, e) =>
        {
            timer.Stop();
            label.Font = new Font(label.Font, label.Font.Style ^ FontStyle.Bold);
        };

        label.Font = new Font(label.Font, label.Font.Style | FontStyle.Bold);
        timer.Start();

        return label;
    }
}

Also i created a second button, a second label and added the following code to the click event:

    private void button2_Click(object sender, EventArgs e)
    {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < LabelExtensions._References.Count; i++)
        {
            var wr = LabelExtensions._References[i];
            sb.AppendLine(i + " alive: " + wr.IsAlive);
        }

        label2.Text = sb.ToString();
    }

Now i just pushed the first button several times to let the first label blink. Then i pressed my second button and got a list where i can see if my timers are still alive. At a first click they all got a true. But then i hit my first button several times again and when i updated my status message i saw that the first items are already got a false in IsAlive. So i can safely say that this function doesn't lead to any memory problems.

烏雲後面有陽光 2024-09-03 10:40:00

您应该通过调用其 Dispose 方法显式地处置 Timer,或者在使用后通过调用 Stop 隐式地处置 Timer,这样您的实现是正确的并且永远不会导致内存泄漏。

You should dispose the Timer explicitly by calling its Dispose method or implicitly by calling Stop after using it, so your implementation is correct and would never cause memory leaks.

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