我想要一个最大保留容量为 N 的缓存。我允许它最多容纳 N 个对象,否则这些对象将有资格进行 GC。现在,如果我的应用程序本身当前持有对先前添加到缓存中的对象的 N+1 强引用,那么我希望缓存也持有 N+1。为什么?因为缓存不会比其他情况下更长时间地阻止第 N+1 个对象被收集,而且我可以用更大的哈希表来换取更多的缓存命中。
另一种说法是,我想要一个对象缓存,它保留添加到其中的所有对象,同时保持强可达性,并且还保留足够的非强可达性对象以保持其大小== N。
示例
我们创建了一个 N=100 的缓存。大小从 0 开始。添加 150 个对象,大小为 150。其中 100 个对象变为非强可达性(弱可达性、弱可达性等)。缓存逐出其中 50 个并保留 50 个,大小为 100。添加了 49 个更强可达对象。大小仍然是 100,但现在其中 99 个是强可达的,只有一个是非强可达的。所发生的情况是 49 个旧的、非强可达对象被新的 49 个对象替换,因为新对象是强可达的。
动机
我怀疑对于许多用例来说这实际上是一个直观的事情。通常,缓存的容量会牺牲缓存命中概率来保证最大内存使用率。了解其所保存的对象的可达性后,缓存可以提供更高的缓存命中概率,而无需更改其最大内存使用保证。
麻烦
我担心这在 JVM 上是不可能的。我希望别人告诉我,但如果你知道事实是不可能的,如果有理由的话,我也会接受这个答案。
最佳答案
您可以将条目添加到配置为 LRU 或 FIFO 缓存的 LinkedHashMap。您也可以拥有 WeakHashMap。如果将 key 添加到两个映射,LHM 将阻止清理,即使它位于 WHM 中。一旦 LHM 丢弃 key ,它可能会也可能不会在 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 后,在尝试追赶时性能不会下降太多。 ;)
关于java - 知道可达性的缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12326847/