[ACCEPTED]-Is there a SoftHashMap in Java?-soft-references
Edit (Aug. 2012):
It turns out that currently 29 the best solution are probably Guava 13.0's 28 Cache
classes, explained on Guava's Wiki - that's what I'm 27 going to use.
It even supports building 26 a SoftHashMap
(see CacheBuilder.newBuilder().softKeys()
), but it is probably not what you 25 want, as Java expert Jeremy Manson explains 24 (below you'll find the link).
Not that I know of (Nov. 2008), but 23 you kind find some implementation of SoftHashMap
on 22 the net.
Like this one: SoftHashMap
or this one.
Edit (Nov. 2009)
As 21 Matthias mentions in the comments, the Google Guava MapMaker does use 20 SoftReferences:
A
ConcurrentMap
builder, providing any 19 combination of these features:
- soft or weak keys,
- soft or weak values,
- timed expiration, and
- on-demand computation of values.
As mentioned 18 in this thread, another JSR166y candidate:
jsr166y.ConcurrentReferenceHashMap
It provides 17 an alternative concurrent reference map 16 to the Google implementation (which relies 15 on a background thread to evict entries)
Edit 14 (August 2012)
The Google implementation uses 13 a background thread only when timed expiration 12 of entries is requested. In particular, it 11 simply uses java.util.Timer
, which is not so intrusive 10 as having a separate background thread.
Jeremy 9 Manson recommends, for any cache, using 8 this feature to avoid the dangers of SoftReference: http://jeremymanson.blogspot.de/2009/07/how-hotspot-decides-to-clear_07.html
There's 7 another implementation from Apache Commons, namely org.apache.commons.collections.map.ReferenceMap; it 6 does not support timed removal, but it does 5 support choosing whether keys should be 4 compared by identity or by equality. Moreover, this 3 implementation is not concurrent - it can 2 be made synchronized, but that works less 1 well under accesses from multiple threads.
I am familiar with two libraries that offer 1 a SoftHashMap implementation:
Apache Commons: org.apache.commons.collections.map.ReferenceMap
Google Collections: com.google.common.collect.ReferenceMap
Have you considered using an LRUMap instead of 2 a soft HashMap? You get more control over 1 what gets stored (or at least, how much).
Apache Shiro comes with a SoftHashMap designed 4 for caching. Its based on the article posted 3 by jb above and licensed under Apache v2. You 2 can find the documentation here and the source 1 code here.
If you want to implement a cache softreferences 34 are definetly a better idea than weak references, but 33 it puts your entire cache removal policy 32 in the hands of the garbage collector. which 31 is probably not what you want.
If cache removal 30 policy is important your are going to need 29 to do it on your own most likely using regular 28 references. However you are going to have 27 to decide when to eject items and which 26 to eject. If you only want to lose things 25 when you are running out of heap space you 24 can query available heap space via:
Runtime.getRuntime().getFreeMemory();
Then 23 once free memory drops below a certain amount 22 you can start either dropping items. Or 21 you could just implement a max size for 20 the cache and use that to decide when to 19 drop things.
here's an LRU cache i designed with O(1) insertion, deletion 18 and lookup time, that has a configurable 17 max number of elements. If you want a cache 16 this is going to be a better solution imho 15 than a SoftHashMap.
The softreferences are 14 a great way to create a growable cache. So 13 the ideal solution would be to use a SoftHashMap 12 along with a regular fixed size cache. have 11 all inserts into the cache go into both 10 the fixed cache and the soft hash map then 9 to reference something just see if its in 8 the soft hashmap (and update the reference 7 time in the cache). this way all your most 6 important items (according to your chosen 5 policy LRU, MFU,...) will never be removed 4 because they are hard referenced in the 3 cache but you will also hold on to more 2 things (with no policy control) as long 1 as there is sufficient memory.
You can use for example this implementation 1 thread-safe Soft reference HashMap:
package cz.b2b.jcl.util;
import java.util.*;
import java.lang.ref.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.AbstractMap.SimpleImmutableEntry;
public class ConcurrentSoftHashMap<K, V> extends AbstractMap {
/**
The internal HashMap that will hold the SoftReference.
*/
private final Map<Object, SoftReference> hash = new ConcurrentHashMap<>();
/**
The number of "hard" references to hold internally.
*/
private final int HARD_SIZE;
/**
The FIFO list of hard references, order of last access.
*/
private final ConcurrentLinkedSetQueue hardCache = new ConcurrentLinkedSetQueue();
/**
Reference queue for cleared SoftReference objects.
*/
private final ReferenceQueue queue = new ReferenceQueue();
public ConcurrentSoftHashMap(int hardSize) {
HARD_SIZE = hardSize;
}
@Override
public Object get(Object key) {
Object result = null;
// We get the SoftReference represented by that key
SoftReference soft_ref = hash.get(key);
if (soft_ref != null) {
// From the SoftReference we get the value, which can be
// null if it was not in the map, or it was removed in
// the processQueue() method defined below
result = soft_ref.get();
if (result == null) {
// If the value has been garbage collected, remove the
// entry from the HashMap.
hash.remove(key);
} else {
// We now add this object to the beginning of the hard
// reference queue.
hardCache.enqueue(result);
if (hardCache.size() > HARD_SIZE) {
// Remove the last entry if list longer than HARD_SIZE
hardCache.dequeue();
}
}
}
return result;
}
/**
Here we put the key, value pair into the HashMap using
a SoftValue object.
@param key
@param value
@return
*/
@Override
public Object put(Object key, Object value) {
processQueue(); // throw out garbage collected values first
return hash.put(key, new SoftValue(value, key, queue));
}
@Override
public Object remove(Object key) {
processQueue(); // throw out garbage collected values first
return hash.remove(key);
}
@Override
public void clear() {
hardCache.clear();
processQueue(); // throw out garbage collected values
hash.clear();
}
@Override
public int size() {
processQueue(); // throw out garbage collected values first
return hash.size();
}
@Override
public boolean containsKey(Object key) {
processQueue(); // throw out garbage collected values first
return hash.containsKey(key);
}
@Override
public Set entrySet() {
Set<Map.Entry> entry = new HashSet<>();
Map.Entry simpleImmutableEntry = null;
Object result = null;
processQueue(); // throw out garbage collected values first
for (Map.Entry<Object, SoftReference> item : hash.entrySet()) {
if (item == null) {
continue;
}
Object key = item.getKey();
SoftReference soft_ref = item.getValue();
if (soft_ref != null) {
result = soft_ref.get();
if (result == null) {
hash.remove(key);
} else {
hardCache.enqueue(result);
if (hardCache.size() > HARD_SIZE) {
hardCache.dequeue();
}
simpleImmutableEntry = new SimpleImmutableEntry(key, result);
entry.add(simpleImmutableEntry);
}
}
}
return entry;
}
private class ConcurrentLinkedSetQueue<E> extends ConcurrentLinkedQueue<E> {
public void enqueue(E o) {
if (!contains(o)) {
add(o);
}
}
public E dequeue() {
return poll();
}
}
/**
We define our own subclass of SoftReference which contains
not only the value but also the key to make it easier to find
the entry in the HashMap after it's been garbage collected.
*/
private static class SoftValue extends SoftReference {
private final Object key; // always make data member final
/**
Did you know that an outer class can access private data
members and methods of an inner class? I didn't know that!
I thought it was only the inner class who could access the
outer class's private information. An outer class can also
access private members of an inner class inside its inner
class.
*/
private SoftValue(Object k, Object key, ReferenceQueue q) {
super(k, q);
this.key = key;
}
}
/**
Here we go through the ReferenceQueue and remove garbage
collected SoftValue objects from the HashMap by looking them
up using the SoftValue.key data member.
*/
private void processQueue() {
SoftValue sv;
while ((sv = (SoftValue) queue.poll()) != null) {
hash.remove(sv.key); // we can access private data!
}
}
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.