可靠地迫使番石榴地图驱逐发生
编辑:我重新组织了这个问题,以反映此后可用的新信息。
这个问题是基于对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。但是,即使能够可靠地强制驱逐,我的问题的第二部分也受到质疑:loadFunction
MyObject
我的问题的第二部分[解决了]:作为对链接问题的响应之一,触摸地图是否可靠地驱逐了所有过期的条目?在链接的答案中,Niraj Tolia表示并非如此,称驱逐可能只能分批处理,这意味着可能需要多次调用触摸地图以确保所有过期对象都被驱逐。他没有详细说明,但是这似乎与根据并发级别将地图拆分为多个段有关。假设我使用了 r10,其中 do 调用了逐出,那么这是针对整个地图,还是只针对其中一个段?containsKey("")
答:maaartinus已经解决了这个问题的这一部分:
请注意,和其他读取方法只运行 ,它除了在每个第64次调用时什么都不做(参见DRAIN_THRESHOLD)。此外,看起来所有清理方法都仅适用于单个段。
containsKey
postReadCleanup
因此,看起来即使在r10中,调用也不是一个可行的修复程序。这使我的问题归结为标题:我如何可靠地强制驱逐发生?containsKey("")
注意:我的 Web 应用明显受到此问题影响的部分原因是,当我实现缓存时,我决定使用多个映射 - 每个类数据对象对应一个映射。因此,对于此问题,有可能执行一个代码区域,导致缓存一堆对象,然后缓存在很长一段时间内不再被触及,因此它不会逐出任何内容。与此同时,对象正在从其他代码区域缓存,内存正在被消耗。我正在这些地图上设置最大尺寸,但这充其量只是一个脆弱的保障措施(我假设它的影响是立竿见影的 - 仍然需要确认这一点)。Foo
Foo
Bar
Baz
更新 1:感谢Darren将相关问题联系起来 - 他们现在有我的投票。因此,看起来解决方案正在酝酿中,但似乎不太可能在r10中。与此同时,我的问题仍然存在。
更新 2:在这一点上,我只是在等待番石榴团队成员对我放在一起的黑客maaartinus提供反馈(见下面的答案)。
最后更新:收到反馈!