2

我想要一个最大保留容量为 N 的缓存。我允许它最多容纳 N 个对象,否则这些对象将有资格进行 GC。现在,如果我的应用程序本身当前持有 N+1 对之前添加到缓存中的对象的强引用,我希望缓存也持有 N+1。为什么?因为缓存不会比其他情况更长时间地阻止这个 N+1th 对象被收集,而且我可以用更大的哈希表来换取更多的缓存命中。

另一种说法是,我想要一个对象缓存,它保留添加到其中的所有对象,同时它们保持强可达性,并且还保留足够的非强可达性对象以保持其大小 == N。

例子

我们创建了一个 N=100 的缓存。大小从 0 开始。添加了 150 个对象,大小为 150。这些对象中的 100 个变得非强可达(弱,软,等等)。缓存驱逐其中的 50 个并保留 50 个,大小为 100。添加了 49 个更强大的可达对象。大小仍然是 100,但现在其中 99 个是强可达的,只有一个是非强可达的。发生的事情是 49 个旧的、非强可达的对象被新的 49 个替换,因为新的对象是强可达的。

动机

我怀疑对于许多用例来说,这实际上是一件直观的事情。通常,缓存的容量会权衡缓存命中概率以保证最大内存使用。了解它所持有的对象的可达性后,缓存可以在不改变其最大内存使用保证的情况下提供更高的缓存命中概率。

麻烦

我担心这在JVM上是不可能的。我希望别人告诉我,但如果你知道一个事实是不可能的,如果有理由的话,我也会接受这个答案。

4

3 回答 3

2

您可以将条目添加到配置为 LRU 或 FIFO 缓存的 LinkedHashMap。你也可以有一个 WeakHashMap。如果将密钥添加到两个映射,LHM 将阻止清理,即使它在 WHM 中。一旦 LHM 丢弃了密钥,它可能在 WHM 中,也可能不在 WHM 中。

例如

private final int retainedSize;
private final Map<K,V> lruMap = new LinkedHashMap<K, V>(16, 0.7f, true) {
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > retainedSize;
    }
};
private final Map<K,V> weakMap = new WeakHashMap<K, V>();

public void put(K k, V v) {
    lruMap.put(k, v);
    weakMap.put(k,v);
}

public V get(K k) {
    V v = lruMap.get(k);
    return v == null ? weakMap.get(k) : v;
}

这样做的原因之一是 WeakHashMap 就像是一下子变得更清晰,所以你的命中率可能会急剧下降。这种方法可确保在您遇到 Full GC 后,您的性能不会因为您试图赶上而下降太多。;)

于 2012-09-08T07:11:03.093 回答
0

退房WeakHashMap。过时的引用将被自动删除。在放置之前,您可以检查大小是否超过阈值并跳过输入新值。

或者,如果大小高于阈值,您可以覆盖 put 并丢弃该值。

此方法将按照您的建议工作,因为您不需要缓存驱逐策略,如果大小大于您的阈值,您可以跳过放入新元素。

于 2012-09-08T00:21:31.073 回答
0

我认为你想要的是有道理的,但也许没有那么多。让我们假设这些值非常大(几千字节),否则其他地方的强保存值的缓存也可能变得昂贵。忽略此开销,您的缓存确实具有恒定的内存成本。但是,我不确定这个目标是否值得追求——我对如何为整个程序使用大约恒定的内存量很感兴趣(我不想留下太多未使用的内存,无论如何我都不想开始交换)。

想法:缓存应该使用注册的弱(或软)引用。1您使用另一个线程ReferenceQueue.remove()在循环中调用并检查某些条件2。根据它,您要么从缓存中删除相应的条目(如 Guava 所做的那样),要么通过恢复值reference.get(),从而暂时保护它不被垃圾收集。3 . 这应该可以,但是在每次 GC 运行期间都会花费一些时间。


1覆盖finalize()也可以。实际上,看起来这是唯一的方法,因为reference.get()入队时总是返回 null,因此不能用于复活。

2条件应该是“每次 GC 运行 100 次”。

3我不确定 GC 是否真的以这种方式工作,但我想确实如此。如果没有,那么您可以使用该值的副本。我也不确定下一次值失去强可达性时会发生什么,但同样,这肯定是可以解决的(例如,创建一个新的参考)。

于 2012-09-08T02:11:29.503 回答