强引用
强引用的对象,永远不会被垃圾回收,JVM 宁愿抛出 OutOfMemeory 错误也不会回收这种对象。
软引用 soft
没有强引用而只有软引用的对象,只要内存空间足够,垃圾回收器就不会回收(严谨的说法是,GC 根据内存使用情况酌情考虑什么时候回收)。
MyObject aStrongRef = new MyObject();
SoftReference aSoftRef = new SoftReference(aStrongRef);
aStongRef = null; // 此时这个对象只有软引用,没有强引用
aStrongRef = aSoftRef.get(); // 返回 null,或者新的强引用
当软引用在 OutOfMemory 之前被回收后,软引用变量不再有存在的价值,但是他们本身还是强引用,太多的软引用变量同样会导致 OutOfMemory 问题。其实,我们可以在创建软引用变量时,指定一个 ReferenceQueue,当软引用变量不再有存在的价值时,会被插入到队列中,我们可以利用此队列回收这些软引用变量。
Set<SoftReference<MyObject>> cache = new HashSet<>();
cache.add(new SoftReference(aStrongRef, queue));
//...
Reference<? extends MyObject> ref = queue.poll();
while (ref != null) {
if (cache.remove(ref)) {
removedSoftRefs++;
}
ref = queue.poll();
}
软引用可以被用于缓存对象,尤其那些重新实例化的开销很大的对象。
弱引用 weak
用来描述非必需对象,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收没有强、软引用而只有弱引用的对象。
WeakHashMap 的特色是使用了弱引用的键,它可以被当做标准 Map 使用,不一样的地方是,当键被 GC 回收时,它会自动清理该键值对。
public class ExampleWeakHashMap {
public static Map<Integer,String> cache = new WeakHashMap<Integer, String>();
public static void main(String[] args) {
Integer i5 = new Integer(5);
cache.put(i5, "five");
i5 = null;
// the entry {5,"five"} will stay in the Map until the next garbage collector call
Integer i2 = 2;
// the entry {2,"two"} will stay in the Map until i2 is no more strongly referenced
cache.put(i2, "two");
// the OutOfMemoryError won't happen, because the Map will clear its entries.
for (int i = 6; i < 100_000_000; i++) {
cache.put(i,String.valueOf(i));
}
}
}
在规范化映射中,可以使用 WeakHashMap<String,Map<K,V>>
保存多个事务的信息,String 类型的键保存事务的 ID,简单的 Map 中保存了事务生命周期中需要的信息,在事务的生命周期中,String 对象的强引用一直存在,所以我们可以一直获取它的信息,当事务结束后,弱引用可以自动帮助我们清理 Map 信息。
虚引用 phantom
在垃圾回收进程中,没有强、软引用的对象会被删除,在被删除前,会先调用对象的 finalize() 方法。当一个对象被 finalized 但是还没有被删除完,它就处于“虚可达”的状态,这意味着只有一个 GC 根节点和该对象之间的一个虚引用。
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> aPhantomRef = new PhantomReference<String>(new String("string"), queue);
和软、弱引用不一样,对一个对象的显式的虚引用会阻止该对象被删除。程序员需要显式或者隐式地移除此虚引用,才能使得 finalized 对象被销毁。显式地销毁虚引用要用到 ReferenceQueue,当虚可达的对象被 finalized 后,队列中会入队此对象。但是虚引用不能获得此对象,get() 方法总是返回 null,程序员不能使虚可达的对象再次强、软、弱可达。这也很好理解,因为在重写的 finalize() 方法中通常要清理各种资源,使得对象不能继续工作。
虚引用的使用场景,可能是,当你需要在对象被 finalized 之后做一些处理,但是又不能重写 finalize() 方法时(处于性能和可靠性的考虑,Effective Java 3 rd 中不建议使用 finalizer 和 cleaner)。