含咖啡因的 LRU

2022-09-04 02:24:36

我正在尝试使用咖啡因作为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}

我做错了什么?


答案 1

咖啡因不实施 LRU 作为其缓存逐出策略。相反,咖啡因使用了一种名为TinyLFU的政策。咖啡因文档包括一个关于效率的页面,其中讨论了这种设计选择的基本原理。引用该页面:

TinyLfu依靠频率草图来概率估计条目的历史使用情况。

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

如果你绝对必须有LRU行为,那么JDK标准LinkedHashMap是一个很好的,直接的选择。您需要对其进行子类化,并使用逻辑覆盖 removeEldestEntry,以便在缓存增长到超出预期时发出信号。如果需要使用多线程,则需要使用适当的同步来包装操作。

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

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

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

您将如何在Java中实现LRU缓存?


答案 2

推荐