不仅仅有强引用
结构
非强引用都继承java.lang.ref.Reference<T>
抽象类
内部包装一个对象引用,以及可选注册一个引用队列
对象垃圾回收后,会把整个引用放入队列,这样外部逻辑就可以知道被回收,可以趁此机会做后续处理
引用队列的好处是可以方便的知道回收行为,如果不使用队列,判断回收需要外部逻辑轮询是否能get出原对象
Reference.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public abstract class Reference <T > { private T referent; volatile ReferenceQueue<? super T> queue; Reference next; Reference(T referent) { this (referent, null ); } Reference(T referent, ReferenceQueue<? super T> queue) { this .referent = referent; this .queue = (queue == null ) ? ReferenceQueue.NULL : queue; } public T get () { return this .referent; } public void clear () { this .referent = null ; } public boolean enqueue () { return this .queue.enqueue(this ); } public boolean isEnqueued () { return (this .queue == ReferenceQueue.ENQUEUED); } }
引用分类
强引用(Strong Reference)
最常见的引用方式。强引用代表对象在被使用,不会被回收
软引用(Soft Reference)
java.lang.ref.SoftReference
一定程度上阻止回收,只要内存还足够,就不会被回收
额外维护时间机制,综合考虑时间和空间因素决定是否回收
时间 clock - timestamp
计算多久没访问
空间 freespace * SoftRefLRUPolicyMSPerMB
计算剩余空间容忍软引用生存时间,直观上看空间越多,对空闲软引用越宽容
SoftReference.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SoftReference <T > extends Reference <T > { static private long clock; private long timestamp; public SoftReference (T referent) { super (referent); this .timestamp = clock; } public SoftReference (T referent, ReferenceQueue<? super T> q) { super (referent, q); this .timestamp = clock; } public T get () { T o = super .get(); if (o != null && this .timestamp != clock) this .timestamp = clock; return o; } }
反射方法调用多次后,JVM会动态生成临时类优化性能,动态类就是使用软引用
弱引用(Weak Reference)
java.lang.ref.WeakReference
不会阻止回收,但是gc线程优先级比较低不一定很快回收掉,提供访问即如果还没回收掉就再次启用
弱引用不会劫持对象,强引用断开,可能get出null
无特殊逻辑,队列用不用都行
WeakReference.java 1 2 3 4 5 6 7 8 9 public class WeakReference <T > extends Reference <T > { public WeakReference (T referent) { super (referent); } public WeakReference (T referent, ReferenceQueue<? super T> q) { super (referent, q); } }
虚引用(Phantom Reference)
java.lang.ref.PhantomReference
形同虚设,不会影响回收,必须配合引用队列,用于跟踪对象GC活动
原引用对象不可访问,get方法返回null,即不能重建强引用
PhantomReference.java 1 2 3 4 5 6 7 8 9 10 11 public class PhantomReference <T > extends Reference <T > { public T get () { return null ; } public PhantomReference (T referent, ReferenceQueue<? super T> q) { super (referent, q); } }
非强引用的作用
强引用下对象不会被回收,如果需要释放对象
没出作用域,还持有对象强引用,解决这种情况方法是手动置为null提前释放
出了作用域,对象还被外围集合强引用持有,解决这种情况方法是手动从集合类移除
非强引用提供了一种柔和的处理方式,如果一个对象上只有非强引用,虽然也是引用但有可能被回收,实现一种自动释放的效果
WeakHashMap
WeakHashMap实现了自动清理,是弱引用的典型应用
普通的HashMap是强引用。即便没有人使用Map内的值,还是被Map强引用着,不会被回收,只能手动从Map中删除
如果忘了手动删除,那么可能内存泄漏
如果删除早了,可能正在被使用中,那么数据丢失
自动清理原理是key利用弱引用自动清理,Entry放入引用队列,用户操作触发Entry清理
底层的Entry实现了WeakReference
,虽然Entry同时有key和value,但从构造函数可知只有key使用了弱引用
当key所引用的对象没有强引用时,key被回收,此时Entry中获取key会得到null
WeakHashMap$Entry.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private static class Entry <K ,V > extends WeakReference <Object > implements Map .Entry <K ,V > { V value; final int hash; Entry<K,V> next; 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; } public K getKey () { return (K) WeakHashMap.unmaskNull(get()); } }
Key被回收后,Entry对象还在,相当于在Map结构中有很多key变成了null值,此时外界通过原来的Key已经无法查出数据
清除Entry这一任务由正常操作触发,比如put
,get
,size
都要获取底层存储数组,而这里会预先清理一遍
Key被回收时,Map的大小并没有发生变化,但是外界调用size时,先进行清理大小发生变化,因此外界可以正确获取当前大小
WeakHashMap.java 1 2 3 4 5 6 7 8 9 10 public int size () { if (size == 0 ) return 0 ; expungeStaleEntries(); return size; } private Entry<K,V>[] getTable() { expungeStaleEntries(); return table; }
清理的过程就是遍历引用队列,然后查找到底层数组,再从链表中移除
从map中移除后,还把Entry的value置为null,彻底解除了map对value的引用关系
WeakHashMap.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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; e.value = null ; size--; break ; } prev = p; p = next; } } } }