我已经基于Rx间隔创建了简单的触发器。它在每个特定的时间间隔生成信号,并通过如下所示的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个实例),并在它们被处置时被丢弃正在被订阅块使用。我想忽略并立即处置它们。
答案 0 :(得分:0)
我认为我能够重现此问题。下面的序列启动一个计时器,该计时器每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
使用的内存几乎呈线性增长,每分钟增加约150 KB。
这里肯定发生了某些事情,但是Timer
运算符的source code太复杂了,我无法确定。我的猜测是内部使用System.Timers.Timer
作为滴答生成器,并且抑制了的滴答以某种方式累积。每个受抑制的滴答声平均泄漏约40个字节。
Windows 10,C#8,.NET Core 3.1.3,System.Reactive 4.4.1