Java 可变大小LRU缓存

Java 可变大小LRU缓存,java,caching,hashtable,lru,Java,Caching,Hashtable,Lru,我正在尝试用Java实现一个LRU缓存,它应该能够: 动态更改大小。从某种意义上说,我计划将其作为SoftReference订阅到ReferenceQueue。因此,根据内存消耗情况,缓存大小会有所不同 我计划使用ConcurrentHashMap,其中值将是软引用,然后定期清除队列以更新映射。 但是上面的问题是,如何使它成为LRU 我知道我们无法控制GC,但我们能否管理对值(缓存中)的引用,使缓存中所有可能的对象都可以根据使用情况(即上次访问时)软访问(在GC下)而不是以某种随机方式。我会使用

我正在尝试用Java实现一个
LRU缓存
,它应该能够:
动态更改大小。从某种意义上说,我计划将其作为
SoftReference
订阅到
ReferenceQueue
。因此,根据内存消耗情况,缓存大小会有所不同

我计划使用
ConcurrentHashMap
,其中值将是软引用,然后定期清除队列以更新映射。
但是上面的问题是,如何使它成为
LRU

我知道我们无法控制GC,但我们能否管理对值(缓存中)的引用,使缓存中所有可能的对象都可以根据使用情况(即上次访问时)软访问(在GC下)而不是以某种随机方式。

我会使用,因为它支持访问顺序,并用作LRU映射。它可以有一个可变的最大大小

根据使用情况在弱引用和软引用之间切换是非常困难的,因为。很难确定a)您的缓存独占使用了多少,b)系统正在使用多少c)在完全GC之后将使用多少


您应该注意到,弱引用和软引用仅在GC上清理,在GC运行之前,丢弃或更改它们不会释放内存。

除非您同时需要几个这样的缓存,否则您的问题没有真正意义。如果只有一个缓存,请不要给它设置大小限制,始终使用
WeakReference
。这样,缓存将自动使用所有可用的可用内存

不过,要准备好与系统管理员进行一些激烈的讨论,因为他们会抱怨你的应用程序内存泄漏,“随时会崩溃!”


另一个选择是使用一个成熟的缓存库,比如说,因为它已经知道了缓存的所有知识,而且他们花了很多年的时间来正确地使用缓存。除非你想花费数年时间调试你的代码,使它能在Java内存模型的每一个角落工作,否则我建议你这次不要重新发明轮子。

弱引用和软引用都不适合这种情况。当对象不再具有更强的引用时,weakreference往往会立即被清除,而软引用只有在堆增长到其最大大小后,以及需要抛出OutOufMemoryError时才会被清除

通常,使用一些基于时间的方法和常规强引用会更有效,这对于VM来说比引用子类便宜得多(对于程序和GC处理更快,并且引用本身不使用额外内存)。也就是说,释放一段时间内未使用的所有对象。您可以使用一个周期性的TimerTask来检查这一点,您无论如何都需要该TimerTask来操作引用队列。其想法是,如果创建对象需要10毫秒,并且在最后一次使用后最多保留1秒,则平均只比永久保留所有对象时慢1%。但由于它很可能会使用更少的内存,因此实际上会更快


编辑:实现这一点的一种方法是在内部使用3个bucket。放置在缓存中的对象始终会插入到bucket 0中。当请求一个对象时,缓存会按顺序在所有3个bucket中查找该对象,如果该对象不在bucket 0中,则将其放入其中。TimerTask以固定的时间间隔被调用,只是丢弃bucket 2并将新的空bucket放在bucket列表的前面,这样新的bucket 0将为空,以前的bucket 0变为1,以前的bucket 1现在为bucket 2。这将确保空闲对象能够在至少一个、最多两个计时器间隔内生存,并且每个间隔访问一次以上的对象能够非常快速地检索。这种数据结构的总维护开销将大大小于基于引用对象和引用队列的所有内容。

问题是:如果LinkedHashMap,我将不得不根据总内存手动更改大小。最后一点,我不明白为什么这会成为一个问题。这只是一个问题,如果你希望它能带来不同。我想我不会增加复杂性,这是没有帮助的。如果不执行完整的GC,就无法知道使用的内存总量,如果不执行另一个GC,就无法强制释放删除的条目。我想我没有正确地传达它。我不想在软弱和软弱之间切换。我想要的是一个具有软引用的缓存(以便它可以相应地缩放),它可以作为lru缓存。你可以使用
LinkedHashMap
?不,它仍然不能成为lru。但是,由于地图的尺寸限制,它确实会使对象持续更长的时间(因此lru会持续一段特定的时间,而不是永远)。但地图的大小仍然是一个更大的问题。PS:为了更清楚,我对问题进行了编辑。我想我没有正确地表达它。我不想在软弱和软弱之间切换。我想要的是一个具有软引用的缓存(以便它可以相应地扩展),它充当lru缓存。然后您必须使用
LinkedHashMap
或编写自己的映射。Java中没有其他地图的大小可以限制。比如说番石榴,使用他们自己的
CacheBuilder
来构建具有一定限制的缓存,因为Java运行时上没有提供足够的控制。因此,基本上LinkedHashMap加上一些额外的努力来确定大小是最好的方法?这种方法花费最少的努力,但仍然返回一些有用的+LRU。如果没有LRU也可以生存,
WeakHashMap
也可以工作。我同意这样的说法:TimerTask或Reference thread将使用相同的时间复杂性,因为两者都必须迭代相同的元素。而且,由于没有太多的内存,它将更容易预测