强制释放 Java 中的大型缓存对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2406349/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Forcing deallocation of large cache object in Java
提问by Hyman
I use a large (millions) entries hashmap to cache values needed by an algorithm, the key is a combination of two objects as a long. Since it grows continuously (because keys in the map changes, so old ones are not needed anymore) it would be nice to be able to force wiping all the data contained in it and start again during the execution, is there a way to do effectively in Java?
我使用一个大(数百万)个条目哈希图来缓存算法所需的值,键是两个对象的组合作为一个长。由于它不断增长(因为地图中的键发生变化,所以不再需要旧的),能够强制擦除其中包含的所有数据并在执行期间重新开始会很好,有没有办法有效地做到这一点在爪哇?
I mean release the associated memory (about 1-1.5gb of hashmap) and restart from the empty hashmap..
我的意思是释放相关的内存(大约 1-1.5gb 的 hashmap)并从空的 hashmap 重新启动..
回答by Thilo
You can call HashMap.clear(). That will remove all data. Note that this will only discard all entries, but keep the internal array used to store the entries at the same size (rather than shrinking to an initial capacity). If you also need to eliminate that, the easiest way would be to discard the whole HashMap and replace it with a new instance. That of course only works if you control who has a pointer to the map.
你可以打电话HashMap.clear()。这将删除所有数据。请注意,这只会丢弃所有条目,但将用于存储条目的内部数组保持在相同的大小(而不是缩小到初始容量)。如果您还需要消除它,最简单的方法是丢弃整个 HashMap 并用新实例替换它。这当然只有在您控制谁拥有指向地图的指针时才有效。
As for reclaiming the memory, you will have to let the garbage collector do its work.
至于回收内存,您将不得不让垃圾收集器完成它的工作。
Are your values also Long? In this case, you may want to look at a more (memory-) efficient implementation than the generic HashMap, such as the TLongLongHashMap found in the GNU Trove library. That should save a lot of memory.
你的价值观也很长吗?在这种情况下,您可能希望查看比通用 HashMap 更(内存)效率更高的实现,例如GNU Trove 库中的 TLongLongHashMap 。那应该会节省很多内存。
回答by polygenelubricants
It sounds like you need a WeakHashMapinstead:
听起来你需要一个WeakHashMap来代替:
A hashtable-based
Mapimplementation with weak keys. An entry in aWeakHashMapwill automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from otherMapimplementations.
Map具有弱键的基于哈希表的实现。当 a 中的条目WeakHashMap不再正常使用时,该条目将被自动删除。更准确地说,给定键的映射的存在不会阻止该键被垃圾收集器丢弃,即使其可终结、终结,然后被回收。当一个键被丢弃时,它的条目被有效地从映射中删除,所以这个类的行为与其他Map实现有些不同。
I'm not sure how this works with Longas keys, though. Also, this might be of interest:
不过,我不确定这是如何Long作为键使用的。此外,这可能很有趣:
WeakHashMap is not a cache! Understanding WeakReference and SoftReference
回答by Thomas Pornin
For a memory-aware cache, you may want to use Apache Commons collections, in particular their org.apache.commons.collections.map.ReferenceMapclass. The Java special operation is a soft reference. Java provides WeakHashMapfor weak references, but weak references are not what you want for a cache. Java does not provide a SoftHashMap, but ReferenceMapfrom Apache Commons can be a workable substitute.
对于内存感知缓存,您可能希望使用Apache Commons 集合,特别是它们的org.apache.commons.collections.map.ReferenceMap类。Java 特殊操作是一个软引用。Java 提供WeakHashMap了弱引用,但是弱引用并不是你想要的缓存。Java 不提供SoftHashMap,但ReferenceMap来自 Apache Commons 的可以是一个可行的替代品。
Memory awareness of soft references is somewhat crude and inflexible. You can play with some Java options to somehow configure them, especially the -XX:SoftRefLRUPolicyMSPerMBvalue, which expresses (in milliseconds) how long soft-referenced values are kept in memory (when they cease to be directly reachable). For instance, with this:
软引用的内存意识有点粗糙和不灵活。您可以使用一些 Java 选项来以某种方式配置它们,尤其是-XX:SoftRefLRUPolicyMSPerMB值,它表示(以毫秒为单位)软引用值在内存中保留多长时间(当它们不再可直接访问时)。例如,与此:
java -XX:SoftRefLRUPolicyMSPerMB=2500
then the JVM will try to keep cached value for 2.5 seconds more than what it would have done with a WeakHashMap.
然后 JVM 将尝试将缓存值保留 2.5 秒,而不是使用WeakHashMap.
If soft references do not provide what you are looking for, then you will have to implement your own cache strategy, and, indeed, flush the map manually. This is your initial question. For flushing, you can use the clear()method, or simply create a new HashMap. The difference should be slight, and you may even have trouble simply measuringthat difference.
如果软引用没有提供您要查找的内容,那么您将必须实现自己的缓存策略,并且确实手动刷新地图。这是你最初的问题。对于刷新,您可以使用该clear()方法,或者简单地创建一个新的HashMap. 差异应该很小,您甚至可能无法简单地测量这种差异。
Alternating between "full cache" and "empty cache" may also be considered as a bit crude, so you could maintain several maps. For instance, you maintain ten maps. When you look for a cached value, you look in all maps, but when you had a value, you put it in the first map only. When you want to flush, you rotate the maps: the first map becomes the second, the second becomes the third, and so on, up to the tenth map which is discarded. A new fresh first map is created. This would look like this:
“完全缓存”和“空缓存”之间的交替也可能被认为有点粗糙,因此您可以维护多个地图。例如,您维护十张地图。当您查找缓存值时,您会查看所有地图,但是当您有一个值时,您只将它放在第一个地图中。当你想要刷新时,你旋转地图:第一张地图变成第二张,第二张变成第三张,依此类推,直到第十张地图被丢弃。创建新的第一张地图。这看起来像这样:
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;
}
}
The code above is completely untested, and should be enhanced with generics. However, it illustrates the main ideas: all maps are tested when reading (get()), all new values go to the first map, the total size is maintained, and when the size exceeds a given limit, maps are rotated. Note that there is some special treatment when a new value is put for a known key. Also, in this version, nothing special is done when finding a cached value, but we could "rejuvenate" accessed cached value: upon get(), when a value is found but not in the first map, it could be moved into the first map. Thus, frequently accessed values would remain cached forever.
上面的代码是完全未经测试的,应该用泛型来增强。但是,它说明了主要思想:读取 ( get())时测试所有地图,所有新值都转到第一个地图,保持总大小,当大小超过给定限制时,旋转地图。请注意,当为已知键放置新值时,会有一些特殊处理。此外,在这个版本中,在查找缓存值时没有做任何特别的事情,但我们可以“恢复”访问的缓存值:on get(),当找到一个值但不在第一个映射中时,它可以移动到第一个映射中。因此,经常访问的值将永远保持缓存状态。
回答by zneak
Clear the hashmap:
清除哈希图:
hashmap.clear();
Then force a garbage collector run:
然后强制垃圾收集器运行:
Runtime.getRuntime().gc();
回答by simonlord
If you have a bit of spare memory you could implement a timout cache where each value in the hashmap contains your long value and an insersion timestamp in millis - then have a background thread iterate over the values every X seconds and remove anything more than X seconds/millis old.
如果您有一点空闲内存,您可以实现一个超时缓存,其中哈希图中的每个值都包含您的长值和以毫秒为单位的插入时间戳 - 然后让后台线程每 X 秒迭代一次这些值并删除超过 X 秒的任何内容/毫秒老。
Just my 2 cents :)
只有我的 2 美分 :)
回答by siegi
Instead of using a HashMapor other map implementation as a cache you could try to use a framework specialized in caching. A well known caching framework for Java is Ehcache.
HashMap您可以尝试使用专门用于缓存的框架,而不是使用或其他地图实现作为缓存。一个著名的 Java 缓存框架是Ehcache。
Caching frameworks let you usually configure expiration policies based on time (e.g. time to live, time to idle) or usage (e.g. least frequently used, least recently used), some may even allow you to specify a maximum amount of memory usage.
缓存框架通常允许您根据时间(例如生存时间、空闲时间)或使用情况(例如最不常用、最近最少使用)配置过期策略,有些甚至可能允许您指定最大内存使用量。
回答by TofuBeer
Have you taken a look at WeakHashMap?
你看过WeakHashMap吗?

