一、Java中的强、软、弱、虚引用
在JDK中我们能够看到有一个java.lang.ref的包。这个包中就是Java中实现强、软、弱、虚引用的包,例如以下:
PhantomReference
虚引用:假设一个对象持有虚引用,就和没有持有引用一样,在不论什么时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收的活动,虚引用另一个和弱、软引用不同的地方是虚引用必须和引用队列联合使用。当垃圾回收器准备回收一个对象时。假设发现它还有虚引用,就会在回收对象内存之前,把这个虚引用加入到与之关联的引用队列中。
程序能够通过推断引用队列中是否已经加入了虚引用,来知道对象是否将要被回收,这样我们就能够在对象被回收之前进行一些操作。
Reference
引用基类:这是一个抽象类。封装了关于引用的相关操作。如去除引用、比較、得到引用对象、推断引用地址是否相等。
ReferenceQueue
引用队列:在垃圾回收器回收对象之前,会将该引用加入到该队列。
SoftReference
软引用:假设内存空间不够用时,垃圾回收器就会回收该引用所引用到的内存对象。使用软引用能够实现内存敏感的快速缓存,软引用能够和引用队列联合使用。
WeakReference
弱引用:当垃圾回收器发现某个对象仅仅有弱引用时不管是否内存够用都会回收该对象,只是。因为垃圾回收器是一个优先级非常低的线程,因此不一定会非常快发现仅仅持有弱引用的对象。弱引用也能够和引用队列联合使用。
二、一个软(弱)引用的样例
以下我们来使用软引用读取一张图片,读出图片的宽和高,代码例如以下:
package 软引用; import java.lang.ref.Reference; /** * 封装引用对象的基类 * @author CodeingSnail * * @param <T> */ public abstract class ReferenceObject<T>{ public Reference<T> ref; protected abstract T getInstance(); protected abstract Reference<T> getReference(T t); /* * 获取引用对象及弱引用 */ private T getRefrenceAndInstance(){ T t = getInstance(); getReference(t); return t; } /** * 获取被引用的对象 * @return */ public T get(){ if(ref == null){ return getRefrenceAndInstance(); } T t = ref.get(); if(t == null){ return getRefrenceAndInstance(); } return t; } /** * 清空对象的引用。回收对象 */ public void recycle() { if(ref != null){ ref.clear(); ref = null; } } }
import java.lang.ref.Reference; import java.lang.ref.SoftReference; import javax.swing.ImageIcon; /** * 引用图片资源的类 * @author Administrator * */ public class ReferenceBitmap extends ReferenceObject<ImageIcon>{ String url; public ReferenceBitmap(String url) { this.url = url; } @Override protected Reference<ImageIcon> getReference(ImageIcon imageIcon) { return new SoftReference<ImageIcon>(imageIcon); } @Override protected ImageIcon getInstance() { return new ImageIcon(url); } }
package 软引用; public class Client { private ReferenceBitmap referenceBitmap; public static void main(String[] args) { Client client = new Client(); client.referenceBitmap = new ReferenceBitmap("E:\test.png"); System.out.println("图片的高度是:" + client.referenceBitmap.get().getIconHeight()); System.out.println("图片的宽度是:" + client.referenceBitmap.get().getIconWidth()); } }在上面样例中我们先定义了一个引用的抽象类,而且给子类提供两个回调方法用来创建图片对象和所须要的软引用(也能够是弱引用),在get()方法中获取对象的实例。事实上上面的样例是一个通用的方法。我们能够在getReference中定义我们须要的引用类型。假如我们如今已经非常明白,我们要的就是软引用,能够将代码简化例如以下:
public class Client { SoftReference<ImageIcon> softRefrence; private ReferenceBitmap referenceBitmap; public static void main(String[] args) { Client client = new Client(); client.referenceBitmap = new ReferenceBitmap("E:\test.png"); System.out.println("图片的高度是:" + client.referenceBitmap.get().getIconHeight()); System.out.println("图片的宽度是:" + client.referenceBitmap.get().getIconWidth()); //简化方法 client.softRefrence = new SoftReference<ImageIcon>(new ImageIcon("E:\test.png")); System.out.println("图片的高度是:" + client.softRefrence.get().getIconHeight()); System.out.println("图片的宽度是:" + client.softRefrence.get().getIconWidth()); } }
三、怎样配合引用队列使用
/** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */ private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next; /** * Creates new entry. */ Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) { super(key, queue); this.value = value; this.hash = hash; this.next = next; } @SuppressWarnings("unchecked") public K getKey() { return (K) WeakHashMap.unmaskNull(get()); } public V getValue() { return value; } public V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?WeakHashMap.Entry继承自WeakReference,在构造方法中能够看到直接将key交给WeakReference并通过ReferenceQueue关联。,?>)o; K k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { V v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public int hashCode() { K k = getKey(); V v = getValue(); return Objects.hashCode(k) ^ Objects.hashCode(v); } public String toString() { return getKey() + "=" + getValue(); } }
/** * Expunges stale entries from the table. */ private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }WeakHashMap中有一个私有的expungeStaleEntries方法。会在大部分共同拥有方法中被调用,这种方法会将ReferenceQueue中全部失效的引用从Map中移除。WeakHashMap不会自己主动释放失效的引用,仅当包括了expungeStaleEntries方法被调用的时候才会释放。以下一个小样例来看一下WeakHashMap的使用:
public static void main(String[] args) { WeakHashMap<String, String> map = new WeakHashMap<String, String>(); map.put(new String("1"), "1"); map.put("2", "2"); String s = new String("3"); map.put(s, "3"); int i = 0; while(map.size() > 0){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Map Size: " + map.size()); System.out.println(map.get("1")); System.out.println(map.get("2")); System.out.println(map.get("3")); if(i == 3) s = null; System.gc(); i++; } }输出结果:
Map Size: 3 1 2 3 Map Size: 2 null 2 3 Map Size: 2 null 2 3 Map Size: 2 null 2 3 Map Size: 1 null 2 null Map Size: 1 null上面的样例中。第一个key外部没有强引用,则仅仅打印了一次就被回收器回收,第三个key有外部的强引用。当我们将外部引用去掉后也被垃圾回收器回收。第二个key是被字符串常量池所引用,所以一直存在。