在恢复收集之前,是否应处理收集中的一次性物品?

本文关键字:集中 一次性 处理 是否 恢复 | 更新日期: 2023-09-27 18:01:38

我有class

class A {
    IObservable<long> poll = new Observable.Interval(100 ms).Do((ms) => LoadData());
    void Subscribe() {
        poll.Subscribe();
    }
}

我有class

class B {
    IEnumerable<A> Items { get; }
    void Refresh() {
        Items = GetNewListWithNewJustCreatedInstances();
    }
}

User多次订阅多个项目,然后调用Refresh()从某个服务器重新加载项目(显然,他再次订阅新项目)。所有旧的订阅会自动处理吗?还是我应该实现类似这样的东西?

class A : IDisposable {
    void Dispose() {
        poll.Dispose();
    }
}
class B {
    void Refresh() {
        foreach (var item in Items) {
            item.Dispose();
        }
    }
}

在恢复收集之前,是否应处理收集中的一次性物品?

你的代码是一个多重内存泄漏的配方。见类似问题。

Rx订阅在被处置、OnComplete调用或OnError之前永远保持存活。垃圾收集不会清理它们。由于没有进行处理,而且Observable.Interval永远不会完成,因此每次订阅都会泄漏,直到内存耗尽为止。您的代码为每个A实例保留了多个订阅的可能性,显然还有多个A对象。

下面是一些对linqpad友好的代码来测试它:

void Main()
{
    var a0 = new A();
    a0.Subscribe();
    a0.Subscribe();
    a0.Subscribe();
    a0.Dispose();
    a0 = null;
    GC.Collect(); //Has no effect. Demonstrates Garbage collection doesn't help.
}
class A : IDisposable
{
    IObservable<long> poll = Observable.Interval(TimeSpan.FromMilliseconds(100)).Do(l => l.Dump());
    IDisposable disposable;
    public void Subscribe()
    {
        Dispose();
        //memory leak!!
        poll.Subscribe();
        //Use this instead
            //disposable = poll.Subscribe();
    }
    public void Dispose()
    {
        disposable?.Dispose();
    }
}

当没有注释内存泄漏行,而注释了安全行时,您将看到每个间隔弹出三个数字,一个数字代表一个订阅。当一次性跟踪行未被注释,并且内存泄漏被注释掉时,您应该看不到任何输出。