4

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

我计划使用ConcurrentHashMapwhere 值将成为软引用,然后定期清除队列以更新地图。
但是上面的问题是,我该怎么做呢LRU

我知道我们无法控制 GC,但是我们是否可以管理对值的引用(在缓存中),使得缓存中所有可能的对象都将根据使用情况(即最后一次)变得可轻松访问(在 GC 下)它被访问了),而不是以某种随机的方式。

4

3 回答 3

4

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

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

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

于 2012-08-03T13:19:43.467 回答
1

除非您同时想要几个这样的缓存,否则您的问题并没有真正的意义。如果你只有一个缓存,不要给它一个大小限制,总是使用WeakReference. 这样,缓存将自动使用所有可用的空闲内存。

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

另一种选择是使用像EHCache这样成熟的缓存库,因为它已经知道所有关于缓存的知识,而且他们花了数年时间才把它们弄好——从字面上看。除非您想花费数年时间调试代码以使其适用于 Java 内存模型的每个角落,否则我建议您这次避免重新发明轮子。

于 2012-08-03T12:58:18.180 回答
0

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

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

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

于 2012-08-03T12:38:03.057 回答