1

我想实现一个缓存,其键和值是软引用,因为它将包含的实例可能需要很长时间来计算(尤其是值),我希望包含未引用对象的条目作为键或值当且仅当我可能内存不足时才能收集垃圾。如果我对这些值使用弱引用,那么一旦它们没有被引用,它们就会被垃圾收集,这不是我想要做的。

起初,我使用了这个例子。它可以工作,但是为每个缓存实例创建一个线程让我很恼火,它对键使用强引用并且我自己在缓存类的某些方法调用中清除过时的条目(如 WeakHashMap)在我冒险运行时不起作用(显然)当我不打电话给他们时,内存不足。我想使用 Guava,但 MapMaker 不再允许使用合乎逻辑的软键,因为默认等价是基于相等 (==) 而不是 equals() 方法,这意味着不可能重新创建相同的键。但是,我同意sebastien-lorber 的评论

如果 Guava 覆盖 SoftReference 的 equals 方法,我认为软键是有意义的。我已经看到 Guava 使用“等价机制”,我认为,对于软引用,defaultEquivalence 不应该是身份,而是等于,来处理这种情况。

我也查看了 MapDB 和 JCS。

如何修改上面引用的示例或使用 Guava 基于软引用进行缓存,最好使用 equals() 而不是 == 来确定键的相等性?

4

2 回答 2

1

我想唯一完美的解决方案是CacheBuilder.softKeysDelegatingEquals按照您的建议使用番石榴。

跳到最后以获得更好的想法

也许这种解决方法可以:

  • 每当你把东西放进去的时候Cache,就把新的SoftReference<Key>放进你自己的HashSet
  • 添加 aremovalListenerCacheSoftReference您的HashSet

无论如何,你不需要线程。

第二部分有点棘手,因为您获得了 aKey并且需要删除其相应的软引用。为此,您可以将 set 替换为 a WeakHashMap<Key, SoftReference<Key>,或者通过让您的 Key 成为equals参考来获得 hacky:

class Key {
    Key(Somedata somedata) {
        this.somedata = somedata;
    }

    @Override public int hashCode() {
        return somedata.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Key) {
            return somedata.equals(((Key) obj).somedata);
        }
        if (obj instanceof KeySoftReference) {
            return equals(((KeySoftReference) obj).get());
        }
        return false;
    }

    @NonNull private final Somedata somedata;
}

// Hacky, never use for anything else.
class KeySoftReference extends SoftReference<Key> {
    protected KeySoftReference(Key key) {
        super(key);
        this.hashCode = key.hashCode();
    }

    @Override public int hashCode() {
        return hashCode;
    }

    @Override public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Key) {
            return ((Key) obj).equals(this);
        }
        if (obj instanceof KeySoftReference) {
            Key key = get();
            // This makes no two cleared reference equal to each other,
            // That's OK as long as you never create two references to the same object.
            return key!=null && key.equals(((KeySoftReference) obj).get());
        }
        return false;
    }

    private final int hashCode;
}

我想,这符合合同,但正如我所说,它是 hacky 和未经测试的。

更新

有简单的解决方案。利用

CacheBuilder.weakKeys().softValues()

并将 a 添加SoftReference<Key>到您的Value. 这使得密钥可以轻松访问。

如果您真的需要比较是平等的,那么您可能不走运,尽管 aWeakInterner<Key>可能会有所帮助。

于 2014-09-04T19:21:44.090 回答
0

实际上,我刚刚在 Guava 中重新引入了软键(在版本 14 中已从其中删除)但在 CacheBuilder 中: https ://code.google.com/p/guava-libraries/issues/detail?id=1845

然后,我必须调用非公共方法 CacheBuilder.keyEquivalence(Equivalence.equals()) 才能使用 equals() 而不是 == 来比较键。我会让你知道它是否有效。在这种情况下,我会接受我自己的答案。否则,我将不得不使用 maaartinus 的建议。

于 2014-09-05T14:14:17.907 回答