性能问答>为什么计时器没有被垃圾收集?>
2回复
5月前

为什么计时器没有被垃圾收集?



写了一个小实验来更好地理解gc。

场景:计时器在设定的时间间隔内调用重复事件。没有对象持有指向计时器的指针。当 GC 发生时,计时器是否停止调用其事件?

假设如果有足够的时间和 GC 尝试,计时器将停止调用其事件。

测试的代码:

using System;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTest;  


[TestClass]
public class TestGC
{

    //starts a repeating timer without holding on to a reference to the timer
    private void StartTimer(Action callback, int interval)
    {
        var timer = new Timer(t => callback());
        timer.Change(interval, interval);
        timer = null;//this probably does nothing, but lets just overwrite the pointer for good measure
    }



    [TestMethod]
    public void TestEventInvoker()
    {
        var count = 0;
        var interval = 100;//time between timer events (ms)
        var totalTime = 50000;//time of the experiment (ms)
        var expectedCalls = totalTime / interval;//minimum number of times that the timer event is invoked if it isn't stopped
        StartTimer(()=>Interlocked.Increment(ref count),interval);

        //gc periodically to make sure the timer gets GC'ed
        for (int i = 0; i < expectedCalls; i++)
        {
            GC.Collect();
            Thread.Sleep(interval);
        }

        //for debugging
        Console.WriteLine($"Expected {expectedCalls} calls. Actual: {count}");

        //test passes if the timer stops before the time is over, and the minimum number of calls is not achieved
        // the -1 accounts for the edge case where the test completes just before the last timer call had a chance to be executed
        Assert.IsTrue(count < (expectedCalls - 1));
    }


}

然后发现计时器继续重复调用其事件,并且增加总时间和 GC 调用次数对停止计时器没有影响。

示例输出:

Assert.IsTrue 失败。
预计 500 个电话。实际:546

所以我的问题是:
为什么计时器会继续触发?
计时器是否被垃圾回收?为什么/为什么不?
如果没有,谁有指向计时器的指针?

220 阅读
请先登录,再评论

这System.Threading.Timer是围绕一些非托管计时器的托管包装。
不调用Dispose托管计时器的实例将被 GC 处理,但其非托管资源仍然会保持活动状态。

5月前

如果你检查定时器的构造函数及其change方法的链接文件源代码,那你会发现在change对 a TimerQueueTimer(间接地,队列属于另一个包装类)的引用期间,会存储在TimerQueue.s_queue.里~

5月前