我已经创建了基于Rx interval的简单触发器。它每隔一个特定的时间间隔生成一个信号,并通过Where方法进行过滤,就像这样(简化的例子):
var isActive = false;
Observable.Interval(TimeSpan.FromMilliseconds(1))
            .Where(_ => !isActive)
            .Subscribe(_ =>
            {
                isActive = true;
                Console.WriteLine("New subscription item");
                Thread.Sleep(30 * 1000); // simulate time-expensive work
                Console.WriteLine("Finished");
                isActive = false;
            });当isActive标志设置为true时,我想忽略元素。它似乎是有效的,因为订阅块每30秒调用一次(模拟耗时的工作),但当我分析这种简单的应用程序时,我看到许多Action对象被存储到内存中(大约每秒60-70个实例),并在它们被订阅块消耗时被释放。我想立即忽略并处理它们。
发布于 2020-12-02 13:00:08
我想我能够重现这个问题。下面的序列启动一个计时器,每10毫秒计时一次。每3秒采样一次,然后在控制台中打印当前的内存使用情况。测试的总持续时间为5分钟。
Observable
    .Interval(TimeSpan.FromMilliseconds(10))
    //.Do(x => Thread.Sleep(1000)) // Uncomment this line to see memory leak
    .Sample(TimeSpan.FromSeconds(3))
    .Select((x, i) => (Sample: i + 1, Ticks: x + 1))
    .Do(x => Console.WriteLine($"Sample #{x.Sample,-2}, Ticks: {x.Ticks,6:#,0}"
        + $", Memory usage: {GC.GetTotalMemory(true):#,0} bytes"))
    .IgnoreElements()
    .Timeout(TimeSpan.FromMinutes(5))
    .Materialize()
    .Wait();下面是输出:
Sample #1 , Ticks:    193, Memory usage: 160,048 bytes
Sample #2 , Ticks:    383, Memory usage: 160,440 bytes
Sample #3 , Ticks:    573, Memory usage: 161,000 bytes
...
Sample #49, Ticks:  9,345, Memory usage: 169,688 bytes
Sample #50, Ticks:  9,537, Memory usage: 169,688 bytes
Sample #51, Ticks:  9,730, Memory usage: 169,688 bytes
...
Sample #97, Ticks: 18,509, Memory usage: 176,120 bytes
Sample #98, Ticks: 18,700, Memory usage: 176,120 bytes
Sample #99, Ticks: 18,892, Memory usage: 176,120 bytes程序使用的内存在整个测试过程中都是稳定的。
现在,让我们取消注释行.Do(x => Thread.Sleep(1000)),它会在1秒内阻止每个节拍的生成。下面是新的输出:
Sample #1 , Ticks:      2, Memory usage: 166,432 bytes
Sample #2 , Ticks:      5, Memory usage: 173,136 bytes
Sample #3 , Ticks:      8, Memory usage: 185,424 bytes
...
Sample #32, Ticks:     95, Memory usage: 367,712 bytes
Sample #33, Ticks:     98, Memory usage: 367,712 bytes
Sample #34, Ticks:    101, Memory usage: 367,712 bytes
...
Sample #65, Ticks:    194, Memory usage: 563,576 bytes
Sample #66, Ticks:    197, Memory usage: 563,576 bytes
Sample #67, Ticks:    200, Memory usage: 563,576 bytes
...
Sample #97, Ticks:    290, Memory usage: 956,792 bytes
Sample #98, Ticks:    293, Memory usage: 956,792 bytes
Sample #99, Ticks:    296, Memory usage: 956,792 bytes使用的内存几乎呈线性增长,大约每分钟增加150KB。
这里肯定发生了一些事情,但是Timer操作符的source code太复杂了,我搞不懂。我的猜测是,System.Timers.Timer在内部用作刻度生成器,它被抑制的刻度是以某种方式累积起来的。对于每个被抑制的节拍,平均泄漏约40个字节。
Windows 10、C# 8、.NET核心3.1.3、System.Reactive 4.4.1
https://stackoverflow.com/questions/51804755
复制相似问题