Implementing a SoftReference based HashMap
Java is slow. Java is a memory hog.
But, if I have to write a network application, I would not hesitate for a second to use Java. Why? Because Java shines in threaded, network-based applications and because over the years, I have recognised the weaknesses of Java and have learnt (the hard way) what I should do, or not do, to avoid running into too serious problems. Recognising the problems takes a while to learn, which is why most Java jobs require you to have at least 2 years of Java programming experience.
One of the most common places of memory wastage is in hash maps, so SUN have provided a WeakHashMap to minimize memory usage in caches implemented using maps. A WeakHashMap stores the keys using WeakReference objects, which means in layman's terms that as soon as the key is not referenced from somewhere else in your program, the entry may be removed and is available for garbage collection. (Have a look at the JavaDocs for the java.util.WeakHashMap and java.lang.ref.WeakReference for more information) Say you write a middle tier that provides an OO view of your database via an application server. What you really want is for the values to be automatically released, rather than the keys. If you put all the objects into a normal HashMap, you can easily run out of memory when you access many different objects from different clients. But if you store the objects in a WeakHashMap they are cleared as soon as your clients is not referencing them anymore. What you really want, however, is to only have the objects released when the VM is running low on memory, since that is when you get problems.
Enter SoftReferences. As far as I understand, a SoftReference will only be garbage collected when the VM is running low on memory and the object it is pointing to is not accessed from a normal (hard) reference. This is probably a better option to use for the HashMap values than a WeakReference, but the default JDK collections don't include a GC-friendly HashMap based on values and neither does it provide a SoftReference based HashMap.
Before I show you a SoftHashMap implementation, based on ideas by Sydney (what's up doc?) Redelinghuys, I need to explain some ideas which will make our SoftHashMap more optimal.
1. Each time we change the map (put, remove, clear) or ask for its size, we first want to go through the map and throw away entries for which the values have been garbage collected. It is quite easy to find out which soft references have been cleared. You can give the SoftReference a ReferenceQueue to which it is added when it is garbage collected.
2. We don't want our hash map to be bullied by the garbage collector, so we provide the option of the map itself keeping a hard link to the last couple of objects (typically 100).
3. The SoftHashMap will use a variant of the Decorator pattern to add this functionality to an internally kept java.util.HashMap. I'm busy working on a Design Patterns course based on the GOF book, let me know if you want further information.
Without further ado, here comes the SoftHashMap:
//: SoftHashMap.java
import java.util.*;
import java.lang.ref.*;
public class SoftHashMap extends AbstractMap {
/** The internal HashMap that will hold the SoftReference. */
private final Map hash = new HashMap();
/** 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 LinkedList hardCache = new LinkedList();
/** Reference queue for cleared SoftReference objects. */
private final ReferenceQueue queue = new ReferenceQueue();
public SoftHashMap() { this(100); }
public SoftHashMap(int hardSize) { HARD_SIZE = hardSize; }
public Object get(Object key) {
Object result = null;
// We get the SoftReference represented by that key
SoftReference soft_ref = (SoftReference)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. One reference can occur more than
// once, because lookups of the FIFO queue are slow, so
// we don't want to search through it each time to remove
// duplicates.
hardCache.addFirst(result);
if (hardCache.size() > HARD_SIZE) {
// Remove the last entry if list longer than HARD_SIZE
hardCache.removeLast();
}
}
}
return result;
}
/** 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!
}
}
/** Here we put the key, value pair into the HashMap using
a SoftValue object. */
public Object put(Object key, Object value) {
processQueue(); // throw out garbage collected values first
return hash.put(key, new SoftValue(value, key, queue));
}
public Object remove(Object key) {
processQueue(); // throw out garbage collected values first
return hash.remove(key);
}
public void clear() {
hardCache.clear();
processQueue(); // throw out garbage collected values
hash.clear();
}
public int size() {
processQueue(); // throw out garbage collected values first
return hash.size();
}
public Set entrySet() {
// no, no, you may NOT do that!!! GRRR
throw new UnsupportedOperationException();
}
}
And here comes some test code that demonstrates to a certain degree that I'm not talking complete bullsh*t. Soft and weak references are quite difficult to experiment with as there is a lot of freedom left to the writers of the JVM of how they must implement them. I wish the implementation would hold back longer before removing these references, that the JVM would wait until it is really running low on memory before clearing them, but unfortunately I am not the one who wrote the JVM. I have tried to question the authors of the java.lang.ref package to find out what the strategy is for references in future versions, but have not had any response yet.
//: SoftHashMapTest.java
import java.lang.ref.*;
import java.util.*;
public class SoftHashMapTest {
private static void print(Map map) {
System.out.println("One=" + map.get("One"));
System.out.println("Two=" + map.get("Two"));
System.out.println("Three=" + map.get("Three"));
System.out.println("Four=" + map.get("Four"));
System.out.println("Five=" + map.get("Five"));
}
private static void testMap(Map map) {
System.out.println("Testing " + map.getClass());
map.put("One", new Integer(1));
map.put("Two", new Integer(2));
map.put("Three", new Integer(3));
map.put("Four", new Integer(4));
map.put("Five", new Integer(5));
print(map);
byte[] block = new byte[10*1024*1024]; // 10 MB
print(map);
}
public static void main(String[] args) {
testMap(new HashMap());
testMap(new SoftHashMap(2));
}
}
Until next week, and please remember to forward this newsletter and send me your comments.
Heinz
分享到:
相关推荐
SoftReference、WeakReference和PhantomReference分析和比较 在 Java 中,引用类型分为强引用、软引用、弱引用和虚引用四种。强引用是我们最常用的引用类型,而软引用、弱引用和虚引用则是 Java 为我们提供的三种...
1. 创建一个图片缓存类,如`ImageCache`,该类内部使用HashMap来存储图片的SoftReference对象,键为图片的URL,值为SoftReference封装的Bitmap对象。 2. 当需要加载图片时,首先检查缓存中是否存在该图片。如果存在...
Map, SoftReference<Bitmap>> imageCache = new HashMap(); // 加载图片到Bitmap对象 Bitmap bitmap = BitmapFactory.decodeStream(inputStream); // 创建SoftReference,将Bitmap对象包装起来 SoftReference...
StrongReference,SoftReference, WeakReference的使用实例,请参照博客:http://blog.csdn.net/To_be_Designer/article/details/72673421
软引用SoftReference1
Java从1.2版本开始引入了四种引用,分别是强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)。这四种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用...
3. 后台任务下载图片完成后,利用SoftReference将Bitmap存储到HashMap,并在UI线程更新ListView的ImageView。 4. 为防止内存泄漏,记得在ListView滚动时清除不再显示的ImageView的Bitmap引用,这可以通过重写...
private HashMap, SoftReference<V>> cache = new HashMap(); public V get(K key) { SoftReference<V> ref = cache.get(key); if (ref != null) { V value = ref.get(); if (value != null) { return value...
- 在 Activity A 中将需要传递的对象添加到 HashMap 中。 - 通过 Intent 或其他方式将字符串键传递给 Activity B。 - Activity B 使用该键从 HashMap 中获取对象。 - 示例: ```java // Activity A dataMap....
两个例子: (1)GridView+异步请求图片 (2)ListView+异步请求图片 用HashMap, SoftReference<Drawable>> 来存储缓存图片,以便再次加载
本篇将详细讲解如何利用软引用(SoftReference)来解决Android OOM问题,并探讨其工作原理以及在实际应用中的注意事项。 **软引用(SoftReference)的概念** 软引用是Java内存模型中的一种特殊引用类型,它介于强...
要实现java缓存有很多种方式,最简单的无非就是static HashMap,这个显然是基于内存缓存,一个map就可以搞定引用对象的缓存,最简单也最不实用,首要的问题就是保存对象的有效性以及周期无法控制,这样很容易就导致...
引用类型如StrongReference、SoftReference、WeakReference和PhantomReference提供不同的对象可达性级别,有助于垃圾收集器管理内存。 Java泛型(Generics)提供编译时类型安全检测和消除类型转换的能力。它支持协...
12. WeakReference与SoftReference: - 弱引用和软引用用于内存管理,当垃圾收集器需要内存时,会清除这些引用的对象。 13. Java 8的流(Stream)和Lambda表达式: - 流提供了新的集合处理方式,可以方便地进行过滤...
this.cacheMap = new HashMap, SoftReference<Bitmap>>(); } public Bitmap loadCacheImage(String imagePath){ Bitmap bitmap = null; if (cacheMap.containsKey(imagePath)) { bitmap = cacheMap.get...
6. **WeakReference与SoftReference**:弱引用和软引用在内存管理中起着重要作用,它们不会阻止垃圾回收,但软引用在内存不足时才会被回收。 7. **Map接口**:除了HashMap和TreeMap,还有LinkedHashMap,它保持插入...
1. 创建图片缓存类:创建一个缓存类,例如`BitmapCache`,它包含一个SoftReference数组或者使用HashMap存储SoftReference。这样可以存储图片对象,同时允许在内存紧张时释放缓存。 2. 实现异步加载:使用AsyncTask...
HashSet基于HashMap实现,存储元素时,通过元素的hashCode定位到桶,然后使用equals方法比较元素是否已经存在。 18. **依赖注入与控制反转**: 依赖注入(DI)是指对象间的依赖关系由容器管理,而不是对象自己...