可靠地迫使番石榴地图驱逐发生

2022-09-02 04:02:37

编辑:我重新组织了这个问题,以反映此后可用的新信息。

这个问题是基于对Viliam关于番石榴地图使用懒惰驱逐的问题的回答:番石榴地图中驱逐的懒惰

请先阅读这个问题及其回答,但基本上的结论是番石榴地图不会异步计算和执行驱逐。给定以下地图:

ConcurrentMap<String, MyObject> cache = new MapMaker()
        .expireAfterAccess(10, TimeUnit.MINUTES)
        .makeMap();

在访问条目后十分钟过去了,它仍然不会被驱逐,直到地图再次被“触摸”。执行此操作的已知方法包括常用的访问器 - 和 和 。get()put()containsKey()

我的问题的第一部分[已解决]:还有哪些其他调用导致地图被“触摸”?具体来说,有谁知道是否属于这一类?size()

想知道这一点的原因是,我已经实现了一个计划任务,偶尔使用这个简单的方法轻推我用于缓存的番石榴地图:

public static void nudgeEviction() {
    cache.containsKey("");
}

但是,我也使用以编程方式报告地图中包含的对象数,以确认此策略是否有效。但是我无法从这些报告中看到差异,现在我想知道是否也会导致驱逐发生。cache.size()size()

答:因此,Mark 指出,在第 9 版中,驱逐仅由 、 和 方法调用,这可以解释为什么我没有看到 .这显然会随着即将发布的下一个版本的番石榴而改变,但不幸的是,我的项目的发布设置得更快。get()put()replace()containsKey()

这使我陷入了一个有趣的困境。通常我仍然可以通过调用来触摸地图,但我实际上使用的是计算地图:get("")

ConcurrentMap<String, MyObject> cache = new MapMaker()
        .expireAfterAccess(10, TimeUnit.MINUTES)
        .makeComputingMap(loadFunction);

其中,从数据库中加载与密钥对应的。看起来我没有简单的方法来强制驱逐,直到r10。但是,即使能够可靠地强制驱逐,我的问题的第二部分也受到质疑:loadFunctionMyObject

我的问题的第二部分[解决了]:作为对链接问题的响应之一,触摸地图是否可靠地驱逐了所有过期的条目?在链接的答案中,Niraj Tolia表示并非如此,称驱逐可能只能分批处理,这意味着可能需要多次调用触摸地图以确保所有过期对象都被驱逐。他没有详细说明,但是这似乎与根据并发级别将地图拆分为多个段有关。假设我使用了 r10,其中 do 调用了逐出,那么这是针对整个地图,还是只针对其中一个段?containsKey("")

答:maaartinus已经解决了这个问题的这一部分:

请注意,和其他读取方法只运行 ,它除了在每个第64次调用时什么都不做(参见DRAIN_THRESHOLD)。此外,看起来所有清理方法都仅适用于单个段。containsKeypostReadCleanup

因此,看起来即使在r10中,调用也不是一个可行的修复程序。这使我的问题归结为标题:我如何可靠地强制驱逐发生?containsKey("")

注意:我的 Web 应用明显受到此问题影响的部分原因是,当我实现缓存时,我决定使用多个映射 - 每个类数据对象对应一个映射。因此,对于此问题,有可能执行一个代码区域,导致缓存一堆对象,然后缓存在很长一段时间内不再被触及,因此它不会逐出任何内容。与此同时,对象正在从其他代码区域缓存,内存正在被消耗。我正在这些地图上设置最大尺寸,但这充其量只是一个脆弱的保障措施(我假设它的影响是立竿见影的 - 仍然需要确认这一点)。FooFooBarBaz

更新 1:感谢Darren将相关问题联系起来 - 他们现在有我的投票。因此,看起来解决方案正在酝酿中,但似乎不太可能在r10中。与此同时,我的问题仍然存在。

更新 2:在这一点上,我只是在等待番石榴团队成员对我放在一起的黑客maaartinus提供反馈(见下面的答案)。

最后更新:收到反馈!


答案 1

我刚刚将方法添加到番石榴中。从 迁移到 后,您可以使用它来强制逐出。Cache.cleanUp()MapMakerCacheBuilder


答案 2

我想知道你在问题的第一部分中描述的同一个问题。从我查看Guava的CustomConcurrentHashMap(第9版)的源代码可以看出,似乎条目在,和方法上被逐出。该方法似乎没有调用逐出。我不是100%确定,因为我快速通过了代码。get()put()replace()containsKey()

更新:

我还在Guava的git存储库中找到了一个最新版本的CustomConcurrentHashmap,看起来它已经更新以调用驱逐。containsKey()

版本9和我刚刚发现的最新版本在调用时都不会调用逐出。size()

更新 2:

我最近注意到(尚未发布)有一个名为CacheBuilder的新类。基本上,这个类是的分叉版本,但考虑到了缓存。该文档表明,它将支持您正在寻找的一些逐出要求。Guava r10MapMaker

我查看了r10版本的CustomConcurrentHashMap中的更新代码,并找到了看起来像计划地图清理器的内容。不幸的是,该代码在这一点上似乎尚未完成,但r10看起来每天都越来越有前途。