强制在Java中重新分配大型缓存对象

我使用大(数百万)条目hashmap来缓存算法所需的值,关键是两个对象的组合作为long。 由于它不断增长(因为地图中的键发生了变化,所以不再需要旧的),能够强行擦除其中包含的所有数据并在执行过程中重新开始会很好,有没有办法有效地执行在Java?

我的意思是释放相关的内存(大约1-1.5gb的hashmap)并从空的hashmap重新启动。

你可以调用HashMap.clear() 。 这将删除所有数据。 请注意,这只会丢弃所有条目,但保留用于以相同大小存储条目的内部数组(而不是缩小到初始容量)。 如果你还需要消除它,最简单的方法是丢弃整个HashMap并用新实例替换它。 当然,只有在您控制谁拥有指向地图的指针时才有效。

至于回收内存,你必须让垃圾收集器完成它的工作。

你的价值观也很长吗? 在这种情况下,您可能希望查看比通用HashMap更高(内存)效率的实现,例如GNU Trove库中的TLongLongHashMap。 这应该可以节省大量内存。

听起来你需要一个WeakHashMap :

具有弱键的基于散列表的Map实现。 当WeakHashMap的密钥不再正常使用时,它将自动被删除。 更确切地说,给定密钥的映射的存在不会阻止密钥被垃圾收集器丢弃,即,可以最终化,最终化,然后回收。 当一个键被丢弃时,它的条目将被有效地从地图中删除,因此该类的行为与其他Map实现略有不同。

不过,我不确定这对于Long是如何工作的。 此外,这可能是有趣的:

WeakHashMap不是缓存! 了解WeakReference和SoftReference

清除hashmap:

 hashmap.clear(); 

然后强制垃圾收集器运行:

 Runtime.getRuntime().gc(); 

这是Runtime.gc()的Javadoc页面 。

对于内存感知缓存,您可能希望使用Apache Commons集合 ,特别是它们的org.apache.commons.collections.map.ReferenceMap类。 Java特殊操作是一个软引用 。 Java为弱引用提供WeakHashMap ,但弱引用不是您想要的缓存。 Java不提供SoftHashMap ,但Apache Commons的ReferenceMap可以作为一种可行的替代品。

软引用的记忆意识有点粗糙和不灵活。 您可以使用某些Java选项以某种方式配置它们,尤其是-XX:SoftRefLRUPolicyMSPerMB值,该值表示(以毫秒为单位)软引用值在内存中保留多长时间(当它们不再可以直接访问时)。 例如,有了这个:

 java -XX:SoftRefLRUPolicyMSPerMB=2500 

那么JVM将尝试将缓存值保持2.5秒,而不是使用WeakHashMap

如果软引用没有提供您要查找的内容,那么您将必须实现自己的缓存策略,并且实际上是手动刷新地图。 这是你最初的问题。 对于刷新,您可以使用clear()方法,或者只是创建一个新的HashMap 。 差异应该很小,甚至可能只是测量差异。

在“完全缓存”和“空缓存”之间交替也可能被认为有点粗糙,因此您可以维护多个映射。 例如,您维护十个地图。 当您查找缓存值时,您会查看所有地图,但是当您有值时,只将其放在第一个地图中。 当你想要刷新时,你可以旋转地图:第一张地图成为第二张,第二张成为第三张,依此类推,直至第十张被丢弃的地图。 创建一个新的第一张新地图。 这看起来像这样:

 import java.util.*; public class Cache { private static final int MAX_SIZE = 500000; private Map[] backend; private int size = 0; public Cache(int n) { backend = new Map[n]; for (int i = 0; i < n; i ++) backend[i] = new HashMap(); } public int size() { return size; } public Object get(Object key) { for (Map m : backend) { if (m.containsKey(key)) return m.get(key); } return null; } public Object put(Object key, Object value) { if (backend[0].containsKey(key)) return backend[0].put(key, value); int n = backend.length; for (int i = 1; i < n; i ++) { Map m = backend[i]; if (m.containsKey(key)) { Object old = m.remove(key); backend[0].put(key, value); return old; } } backend[0].put(key, value); size ++; while (size > MAX_SIZE) { size -= backend[n - 1].size(); System.arraycopy(backend, 0, backend, 1, n - 1); backend[0] = new HashMap(); } return null; } } 

上面的代码是完全未经测试的,应该使用generics来增强。 但是,它说明了主要思想:所有地图在阅读时都会经过测试( get() ),所有新值都会转到第一张地图,总尺寸会保持不变,当尺寸超过给定限制时,地图会被轮换。 请注意,为已知密钥设置新值时会有一些特殊处理。 此外,在这个版本中,找到缓存值时没有什么特别的,但是我们可以“恢复”访问的缓存值:在get() ,当找到一个值而不是在第一个映射中时,它可以被移动到第一个地图。 因此,经常访问的值将永远保持缓存。

你看过WeakHashMap了吗?

如果你有一些备用内存你可以实现一个timout缓存,其中hashmap中的每个值都包含你的long值和一个毫秒的破坏时间戳 – 然后有一个后台线程每X秒迭代一次值并删除任何超过X秒的值/毫老。

只是我的2美分:)

您可以尝试使用专门用于缓存的框架,而不是使用HashMap或其他地图实现作为缓存。 一个众所周知的Java缓存框架是Ehcache 。

缓存框架通常允许您根据时间(例如,生存时间,空闲时间)或使用情况(例如,最不常使用,最近最少使用)配置到期策略,有些甚至可以允许您指定最大内存使用量。