java - 含咖啡因的 LRU

标签 java lru caffeine

我正在尝试使用 Caffeine 作为 LRU 缓存,因此首先添加的条目将首先被逐出。 运行这段代码:

final Cache<Object, Object> map = Caffeine.newBuilder()
            .maximumSize(10)
            .initialCapacity(10)
            .build();

for (long i=0; i<20;i++) {
        map.put(i, i);
}

map.cleanUp();
System.out.println(map.ge.getAllPresent(map.asMap().keySet()));

打印:

{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 19=19}

但我预料到了

{10=10, 11=11, 12=12, 13=13, 14=14, 15=15, 16=16, 17=17, 18=18, 19=19}

我做错了什么?

最佳答案

Caffeine 没有实现 LRU 作为其缓存逐出策略。相反,Caffeine 使用名为 TinyLFU 的策略. Caffeine 文档包括关于 Efficiency 的页面,其中讨论了这种设计选择的基本原理。引用该页面:

TinyLfu relies on a frequency sketch to probabilistically estimate the historic usage of an entry.

由于 Caffeine 实际上并没有实现 LRU,我认为当您检查缓存中的条目时,您不能可靠地期望它表现出严格的 LRU 行为。

如果您绝对必须具有 LRU 行为,那么 JDK 标准 LinkedHashMap是一个很好的、直接的选择。您需要对其进行子类化并覆盖 removeEldestEntry当缓存变得比你想要的大时,逻辑会发出信号。如果需要使用多线程,则需要使用适当的同步来包装操作。

咖啡因深受 Guava Cache 启发,它同样提供并发访问并具有近似 LRU 行为。针对 Guava 缓存对您的代码进行的快速测试显示了类似的结果。我不知道有任何标准库可以提供可预测的、外部可观察的 LRU 结果和真正的并发访问而无需粗粒度锁。

您可能会重新考虑是否真的需要严格的、外部可观察的 LRU 结果。就其本质而言,缓存是提供优化查找的快速临时存储。我不认为程序行为会根据缓存是否实现严格的 LRU、LRU、LFU 的近似值或其他一些逐出策略而发生巨大变化。

这个先前的问题也对 Java 中的 LRU 缓存选项进行了很好的讨论。

How would you implement an LRU cache in Java?

关于java - 含咖啡因的 LRU,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34694920/

相关文章:

java - 如何使用 javascript 知道安装的 jvm 是 32 位还是 64 位

c - C 中的 LRU 缓存设计,大小有限

python - 可以从 functools.lru_cache 替换或删除特定键吗?

java - Caffeine Expiry 中如何设置多个过期条件?

java - 获取 jsonarray 键名

java - 从 Nullable 对象创建 Stream 的惯用方法

gradle - Gradle构建问题:在Caffeine Simulator中无法获取未知属性 'libraries'

java - 为什么 Collection 不被简单地视为 Collection<?>

java - 如果数据频繁变化,应该使用什么类型的java缓存?