使用键和值的软引用实现高速缓存

时间:2014-09-04 13:35:06

标签: java caching guava soft-references

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

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

  

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

我也看了MapDB和JCS。

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

2 个答案:

答案 0 :(得分:1)

我想唯一完美的解决方案是在你提出的番石榴中加CacheBuilder.softKeysDelegatingEquals

跳到最后以获得更好的主意

也许这种解决方法可以:

  • 每当您在Cache中添加内容时,请将新SoftReference<Key>放入您自己的HashSet
  • removalListener添加Cache,然后从SoftReference
  • 中删除HashSet

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

第二部分有点棘手,因为您需要Key并需要删除其相应的软引用。为此,您可以使用WeakHashMap<Key, SoftReference<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;
}

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

更新

有一个简单的解决方案。使用

CacheBuilder.weakKeys().softValues()

并向SoftReference<Key>添加Value。这使得密钥可以轻松到达。

如果你真的需要比较是平等的,那么你可能会运气不好,虽然WeakInterner<Key>可以提供帮助。

答案 1 :(得分:0)

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

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