Rx-哪里方法导致内存泄漏?

时间:2018-08-12 00:19:30

标签: c# memory-leaks where system.reactive

我已经基于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个实例),并在它们被处置时被丢弃正在被订阅块使用。我想忽略并立即处置它们。

1 个答案:

答案 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