我使用一个包含数百万条目的哈希映射表来缓存算法所需的值,键是两个对象的组合形成的长整型。由于它不断增长(因为映射表中的键发生变化,老键已不再需要),因此在执行过程中强制清除其中的所有数据并重新开始将是不错的选择,在Java中是否有一种有效的方法实现这一点?
我的意思是释放关联的内存(约1-1.5GB的哈希映射表),并从空的哈希映射表重新启动。
我使用一个包含数百万条目的哈希映射表来缓存算法所需的值,键是两个对象的组合形成的长整型。由于它不断增长(因为映射表中的键发生变化,老键已不再需要),因此在执行过程中强制清除其中的所有数据并重新开始将是不错的选择,在Java中是否有一种有效的方法实现这一点?
我的意思是释放关联的内存(约1-1.5GB的哈希映射表),并从空的哈希映射表重新启动。
HashMap.clear()
。这将删除所有数据。请注意,这只会丢弃所有条目,但保留用于存储条目的内部数组的大小(而不是缩小为初始容量)。如果您还需要消除它,最简单的方法是丢弃整个HashMap并用新实例替换它。当然,这仅适用于您控制谁有指向该地图的指针。听起来你需要使用WeakHashMap:
WeakHashMap
是基于哈希表的Map
实现,它具有弱引用键。当一个键不再被正常使用时,WeakHashMap
中相应的条目将自动删除。更准确地说,在给定键的映射存在时,该键不会阻止垃圾回收器废弃该键,即使经过最终化并回收后也是如此。当一个键已被废弃时,其条目在实际上从地图中删除,因此这个类的行为与其他Map
实现略有不同。
不过,我不确定这对于以Long
作为键值是否有效。此外,你可能会对以下内容感兴趣:
gc()
永远不是一个好建议,但我可能错了。 - polygenelubricantsorg.apache.commons.collections.map.ReferenceMap
类。Java的特殊操作是软引用。Java提供了WeakHashMap
用于弱引用,但弱引用不适合用于缓存。Java没有提供SoftHashMap
,但是来自Apache Commons的ReferenceMap
可以成为可行的替代品。-XX:SoftRefLRUPolicyMSPerMB
值,该值表示(以毫秒为单位)保留软引用值在内存中的时间(当它们不再直接可达时)。例如,使用以下内容:java -XX:SoftRefLRUPolicyMSPerMB=2500
如果使用软引用无法满足您的需求,那么您将需要实现自己的缓存策略,并手动清空映射。这是您最初的问题。对于清空操作,您可以使用clear()
方法,或者简单地创建一个新的HashMap
。两种方法的区别应该很小,您甚至可能难以简单地测量到这种区别。
在“完整缓存”和“空缓存”之间交替也可能被认为有点粗糙,因此您可以维护多个映射。例如,您可以维护十个映射。当您查找缓存值时,您会在所有映射中查找,但当您拥有一个值时,您只将其放入第一个映射中。当您想要清空时,您旋转映射:第一个映射变为第二个映射,第二个映射变为第三个映射,依此类推,直到第十个映射被丢弃。然后创建一个新的第一个映射。这看起来像是这样的:
如果JVM使用WeakHashMap
,则它将尝试保留缓存值比原本多2.5秒。
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;
}
}
get()
)时测试所有映射,所有新值都进入第一个映射,总大小保持不变,并且当大小超过给定限制时,旋转映射。请注意,当为已知的键放置新值时,需要做一些特殊处理。此外,在此版本中,在找到缓存的值时没有执行任何特殊操作,但是我们可以“恢复”访问的缓存值:在 get()
时,当找到一个值但不在第一个映射中时,可以将其移动到第一个映射中。因此,经常访问的值将永远保留在缓存中。clear()
意味着您保留数组,但避免以后重新分配它,并且在内部数组增长时避免一定程度的重新哈希。我认为从长远来看,这不会产生可衡量的差异。 - Thomas Pornin仅供参考 :)
不要使用HashMap
或其他地图实现作为高速缓存,您可以尝试使用专门用于缓存的框架。 Java中一个众所周知的缓存框架是Ehcache。
缓存框架通常允许您根据时间(例如存活时间、空闲时间)或使用情况(例如最不经常使用、最近最少使用)配置过期策略,一些甚至允许您指定最大内存使用量。
你看过WeakHashMap了吗?