Java固定内存映射

时间:2010-05-31 03:50:58

标签: java caching dictionary memory-management fixed

是否有一个简单,高效的Map实现,允许地图使用内存限制。

我的用例是我想动态分配创建时可用的大部分内存,但我不希望将来随时OutOFMemoryError。基本上,我想将此映射用作缓存,但我想避免像EHCache这样的繁重缓存实现。我的需求很简单(最多是一个LRU算法)

我应该进一步澄清我的缓存中的对象是char[]或类似的原语,它们不会保存对其他对象的引用。

我可以为每个条目设置最大大小的上限。

5 个答案:

答案 0 :(得分:9)

您可以使用LinkedHashMap来限制Map中的条目数:

  

removeEldestEntry(Map.Entry<K,V> eldest):如果此地图应删除其最长条目,则返回true。在将新条目插入地图后,putputAll将调用此方法。它为实现者提供了在每次添加新条目时删除最旧条目的机会。如果映射表示缓存,这将非常有用:它允许映射通过删除过时条目来减少内存消耗。

     

示例使用:此覆盖将允许地图增长到100个条目,然后在每次添加新条目时删除最旧的条目,保持100个条目的稳定状态。

private static final int MAX_ENTRIES = 100;

protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
}

相关问题

答案 1 :(得分:6)

对于缓存,SoftHashMapWeakHashMap更合适。当您想要保持与对象的关联时,通常使用WeakhashMap,只要该对象处于活动状态,但不会阻止它被回收。

相比之下,SoftReference更密切地涉及内存分配。有关差异的详细信息,请参阅No SoftHashMap?

WeakHashMap通常也不合适,因为它具有围绕错误的缓存方式的关联 - 它使用弱键和硬值。也就是说,当垃圾收集器清除时,键和值将从地图中删除。这通常不是您想要的缓存 - 其中键通常是轻量级标识符(例如字符串或其他一些简单的值类型) - 缓存通常操作使得当值参考被清除。

Commons Collections有一个ReferenceMap,您可以在其中插入您希望用于键和值的引用类型。对于对内存敏感的缓存,您可能会使用硬引用键和值的软引用。

要获取给定数量的引用N的LRU语义,请维护从缓存中获取的最后N个条目的列表 - 当从缓存中检索条目时,它将被添加到列表的头部(以及删除列表。)为了确保不会占用太多内存,您可以创建一个软引用,并将其用作触发器,从列表末尾逐出百分比的条目。 (并为下一次触发创建一个新的软参考。)

答案 2 :(得分:3)

Java平台解决方案

如果你要找的是一张地图,可以清理其密钥以避免OutOfMemoryErrors,你可能需要查看WeakHashMap。它使用WeakReferences以允许垃圾收集器获取映射条目。但是,它不会强制执行任何类型的LRU语义,除了代际垃圾收集中存在的那些。

还有LinkedHashMap,在文档中有这个:

  

提供了一个特殊的构造函数   创建一个其顺序的链接哈希映射   迭代的顺序是它的顺序   上次访问的条目来自   最近访问过的   最近(访问顺序)。这个   一种地图非常适合建筑   LRU缓存。调用put或get   方法导致访问   相应的条目(假设它   调用后存在   完成)。 putAll方法   为每个生成一个条目访问权限   映射在指定的映射中   命令键值映射   由指定地图的条目提供   设置迭代器。没有其他方法   生成入口访问。在   特别是,开展业务   集合视图不会影响   支持地图的迭代顺序。

因此,如果您使用此构造函数创建Iterator在LRU中迭代的地图,则修剪地图变得非常容易。一个(相当)警告是LinkedHashMap 无法同步,因此您可以自己进行并发。您可以将其包装在同步包装器中,但这可能会产生吞吐量问题。

自行解决方案

如果我必须为这个用例编写自己的数据结构,我可能会创建一些带有map,queue和ReadWriteLock的数据结构以及一个janitor线程来处理清理时的情况。地图中有太多条目。有可能略微超过所需的最大尺寸,但在稳定状态下,你会留在它下面。

答案 3 :(得分:1)

WeakHashMap不一定会达到您的目的,因为如果您的应用程序持有对密钥的足够强引用,您将会看到OOME。

或者你可以查看SoftReference,一旦堆稀缺,它将使内容无效。但是,我看到的大多数注释表明它不会使引用无效,直到堆非常低并且很多GC开始以严重的性能命中(因此我不建议将其用于您的目的) 。

我的建议是使用简单的LRU地图,例如http://commons.apache.org/collections/apidocs/org/apache/commons/collections/LRUMap.html

答案 4 :(得分:0)

感谢回复家伙们!

正如jasonmp85所指出的,LinkedHashMap有一个允许访问顺序的构造函数。当我查看API文档时,我错过了那一点。实施也看起来非常有效(见下文)。结合每个条目的最大大小上限,这应该可以解决我的问题。

我还将仔细研究SoftReference。仅作为记录,Google Collections似乎拥有相当不错的SoftKeys和SoftValues以及地图的API。

这是Java LikedHashMap类的一个片段,展示了它们如何维护LRU行为。

    /**
     * Removes this entry from the linked list.
     */
    private void remove() {
        before.after = after;
        after.before = before;
    }

    /**
     * Inserts this entry before the specified existing entry in the list.
     */
    private void addBefore(Entry<K,V> existingEntry) {
        after  = existingEntry;
        before = existingEntry.before;
        before.after = this;
        after.before = this;
    }

    /**
     * This method is invoked by the superclass whenever the value
     * of a pre-existing entry is read by Map.get or modified by Map.set.
     * If the enclosing Map is access-ordered, it moves the entry
     * to the end of the list; otherwise, it does nothing.
     */
    void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) {
            lm.modCount++;
            remove();
            addBefore(lm.header);
        }