【问题标题】:Should disposable items in collection be disposed before collection is reinstainated?收集中的一次性物品是否应在恢复收集前处理掉?
【发布时间】:2016-08-09 18:59:05
【问题描述】:

我有课

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

    void Subscribe() {
        poll.Subscribe();
    }
}

我有课

class B {
    IEnumerable<A> Items { get; }

    void Refresh() {
        Items = GetNewListWithNewJustCreatedInstances();
    }
}

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

class A : IDisposable {
    void Dispose() {
        poll.Dispose();
    }
}

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

【问题讨论】:

  • 如果您没有要在此处解决的特定问题,只需让垃圾收集器完成其工作即可。
  • @EdPlunkett 我不是要解决问题,我只是担心我的计时器会在我点击 Refresh() 方法后每 100 毫秒继续执行一次,并且每次我点击 Refresh( ) 这些订阅的数量将会增加。因为我处理 items,所以我只是用 new 项重新创建和重新填充集合(如果用户在某个变量中引用了旧项,我假设它会活着)。
  • 这应该很容易测试。如果它一直开火......是的,你需要拍打他们。
  • 是的,我想。可惜现在没有测试环境,只有代码:)明天测试。
  • @EdPlunkett - GC 从不调用 dispose。 GC 可能调用一个调用 dispose 的析构函数,但你不能保证它会。您必须明确检查对象是否具有调用 dispose 的析构函数。

标签: c# .net system.reactive disposable


【解决方案1】:

您的代码会导致多次内存泄漏。见similar question

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();
    }
}

当内存泄漏行未注释并且安全行已注释时,您会看到每个间隔弹出三个数字,每个订阅一个。当disposable-tracking 行取消注释并且内存泄漏被注释掉时,您应该看不到任何输出。

【讨论】:

  • 如果我用 A 类的新实例集重新初始化 IEnumerable,但没有调用所有旧实例 a.Dispose(),垃圾收集器会自动处理 A 类吗?
  • 问题不在于A 类的实例。问题是订阅的实例。想象一下poll.Subscribe 返回一个不会作为垃圾收集的对象,因为有一些神秘的内部类持有对它的强引用,只会在Dispose 上被丢弃。所以你的场景可能有垃圾收集清理 A 实例,但不是订阅实例。
  • @EwanCoder - GC 从不调用.Dispose()。唯一的(某种)例外是,如果您知道有一个终结器显式调用 .Dispose(),那么这个 可能 会被 GC 调用。 Rx 订阅没有具有终结器,因此 GC 不会为您处理它们。
猜你喜欢
  • 2010-10-29
  • 2018-03-21
  • 2013-12-27
  • 2021-05-22
  • 2016-01-01
  • 2012-12-20
  • 2015-02-21
  • 2016-02-29
  • 1970-01-01
相关资源
最近更新 更多