强引用和弱引用
强引用和弱引用主要区别在于什么时候被垃圾回收,强引用就是我们最常使用的类型,在垃圾回收的时候,如果一个对象仅仅存在它的弱引用,那么它会被无情的回收掉。
Integer key = 666; // 强引用
WeakReference<Integer> value = new WeakReference<>(888); // 弱引用
// 另外,Integer内部IntegerCache保留了-128~127的强引用
System.gc();
// 此时key还是666 但是value = null
在上一节中的源码深入,我发现ThreadLocalMap的key是弱引用,但是value却是个强引用,当时没想明白就没写了,现在想明白了。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
// key在这里被变成了弱引用,方便回收
super(k);
value = v;
}
}
先说结论:key是弱引用,是因为一旦ThreadLocal<?>被回收了,那么就意味着当前线程已经不需要这个变量了,key失去了ThreadLocal<?>的强引用,就只剩ThreadLocalMap的entry的弱引用,就会被gc掉了。如果key是强引用,那么即使ThreadLocal被回收了没了,但是此时ThreadLocalMap的entry还保留着对key的强引用,那它永远不会被回收,除非线程被回收了(因为threaLocals是当前线程的成员变量)。value是强引用,这是因为key弱引用但是key引用的对象同时有ThreadLocal<?>作为强引用,在弃用前不会被gc掉,但是value没有,在set完之后如果value是弱引用,引用对象就只剩value这个弱引用了,下次gc就会被回收了。因此把value作为强引用,但是这会造成泄露的问题,不过ThreadLocalMap似乎也考虑到了,帮助我们时不时的清理key为null的entry。
上面实线代表强引用,虚线弱引用。理解之后我们看下面这个例子,帮助更深入理解一下。
public class TestWeakReference {
public static void setMap(Map map, boolean weak, WeakReference<Integer> key) {
// weak标注一下是不是弱引用
if (weak) {
WeakReference<Integer> value = new WeakReference<>(888);
map.put(key, value);
} else {
map.put(key, 888);
}
// 静态方法手动回收一下
System.gc();
}
public static void main(String[] args) throws Exception {
Map<WeakReference<Integer>, WeakReference<Integer>> weakmap = new HashMap<>();
Map<WeakReference<Integer>, Integer> map = new HashMap<>();
WeakReference<Integer> key = new WeakReference<>(666);
setMap(weakmap, true, key);
setMap(map, false, key);
System.out.println("从弱引用value的map中get: " + weakmap.get(key).get());
System.out.println("从强引用value的map中get: " + map.get(key));
}
}
最近在学习SpringSecurity,结合SecurityContextHolder也明白了value必须是强引用,否则鉴权完之后,value就不存在强引用会被回收的。