【问题标题】:Cleaning up a cache of .net WeakReferences清理 .net WeakReferences 的缓存
【发布时间】:2015-04-21 13:52:06
【问题描述】:

在我的应用程序中,我有一个Dictionary<int, WeakReference<Foo>> 来缓存从文件中读取的Foos,其中键是文件中的索引。由于Foos 是可变的,只要在该索引处对Foo 的任何引用,缓存条目就必须保持活动状态(以便任何持有者以及从Foo 源中读取新内容的任何人都可以看到更改) .

一旦Foo 完全未引用,我想删除Dictionary 条目。我最初的想法是让 Foo 的终结器将自己从缓存中删除,但这会导致在缓存插入时触发 GC 时内部状态不一致。我试图牢记终结器不能用于托管内存的概念。那么有没有办法做到这一点呢?

【问题讨论】:

  • 什么都不做怎么样,让 GC 和 WeakReference 做他们的事情?
  • GC 会清理字典中的Foos,但不会清理KeyValuePair<int, WeakReference<Foo>>s。
  • 我知道,但这只是很小的内存量。当您关闭文件/字典对时,所有这些都会被回收。

标签: .net caching garbage-collection weak-references finalizer


【解决方案1】:

在我看来,您正在寻找的是 ConditionalWeakTable<TKey, TValue>,您将 Foo 存储为您的密钥,而不是您的值:

ConditionalWeakTable 类不同于其他类 集合对象在其管理对象生命周期中的键 存储在集合中。通常,当一个对象存储在 集合,它的生命周期一直持续到它被删除(并且没有 对对象的附加引用)或直到集合对象 本身就被破坏了。但是,在 ConditionalWeakTable 类中,向表中添加键/值对并不能确保 即使可以直接从 存储在表中的值(例如,如果表包含一个键, A,值为 V1,第二个键 B,值为 P2,包含 参考 A)。相反,ConditionalWeakTable 一旦没有其他条目,就会自动删除键/值条目 对键的引用存在于表之外。

【讨论】:

  • 我需要能够通过 int 索引进行查找。
  • 创建一个hack可能是使用Dictionary<int, ConditionalWeakTable<Foo, WhatEver>,然后从条件表中提取Foo。但那是丑陋的..
  • 那根本没有帮助。我想在收集时从字典中删除条目。
  • @SheaLevy 让我问你这个,你为什么关心清理字典?如果您担心的是内存,WeakReference 大约是 16 字节。其中有 500,000 个大约是 8MB。你真的担心吗?
  • @SheaLevy 你总是可以有一个专用线程每隔一段时间收集一个集合来清理死对象..
【解决方案2】:

我最终通过一个名为BackgroundFinalizer 的项目解决了这个问题(来源github)。本质上,这允许您定义在垃圾收集期间不运行而是在后台运行的“终结器”。所以在我这里的特殊情况下,当Foo 被垃圾收集时,一个从缓存中删除条目的函数被安排在独立于 GC 的工作线程中运行,因此我们避免了任何在终结器中阻塞或内部不一致的情况状态。

【讨论】:

  • 所有终结器都在后台运行...这似乎与 ~Foo() 没有什么不同
  • 我的描述/命名可能很糟糕,但它在后台相对于 GC。也就是说,如果它阻塞它不会阻止继续收集,如果 GC 在某些对象的内部状态不确定时运行,它对终结器将不可见,等等。
  • 你的Foos 一定很不稳定......终结者的指导方针是不要抛出而不是阻止。如果你能解决这个问题,它会更可靠。
  • 这与Foo 无关,它与缓存有关。将元素插入缓存可以触发 gc,它可以在与插入相同的线程上运行终结器。除非字典是无锁的,否则意味着不一致的内部状态或死锁。
  • “在与插入相同的线程上运行终结器。”不,这不会发生。
【解决方案3】:

由于无法在对象被取消引用时自动删除 Dictionary 条目,因此另一个想法是在了解它后立即自行删除它,方法是编写这样的缓存获取函数:

private readonly Dictionary<int,WeakReference<Foo>> _dict;
public bool TryGetItem(int key, out Foo item) {
    if (_dict.TryGetvalue(key, out var wr)) {
        if (wr.TryGetTarget(out item))
            return true;
        else
            // The weak reference is dead. We remove the dictionary entry.
            _dict.Remove(key);
    }
    return false;
}

【讨论】:

    猜你喜欢
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-02
    • 2012-01-08
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多