强-软-弱-虚引用

232 阅读4分钟

1. 强引用

所有对象的引用默认为强引用,普通代码中,赋值语句之间传递的都是强引用,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,即使内存不足,JVM也会直接抛出OutOfMemoryError。除非显示的将强引用赋值为null,切断对象引用,使对象不可达。

Object obj = new Object(); 
obj = null;  

2. 软引用

在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,可以用作缓存。

private static List<Object> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
        byte[] buff = new byte[1024 * 1024];
        SoftReference<byte[]> sr = new SoftReference<>(buff);
        list.add(sr);
}
System.out.println(list.get(0)); //正常返回
System.gc(); //主动通知垃圾回收
System.out.println(list.get(0)); //返回null

3. 弱引用

无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。
使用别叫广泛,下面详细讲下在ThreadLocal和JDK动态代理中的应用。

ThreadLocal

ThreadLocal使用代码

public static void main(String[] args) {
    ThreadLocal<String> threadLocal = new ThreadLocal<>();

    Thread thread1 = new Thread(()->{
        System.out.println("thread1:"+threadLocal.get());
    });
    thread1.start();

    Thread thread2 = new Thread(()->{
        threadLocal.set("hello-2");
        System.out.println("thread2:"+threadLocal.get());
    });
    thread2.start();

    Thread thread3 = new Thread(()->{
        threadLocal.set("hello-3");
        System.out.println("thread3:"+threadLocal.get());
    });
    thread3.start();

}

ThreadLocal弱引用.png 图片看不清请右键在新标签页打开图片

无论是ThreadLocal的set方法还是get方法,都是先调用Thread.currentThread()获取当前Thread,
然后调用getMap方法,得到当前线程的ThreadLocal.ThreadLocalMap threadLocals属性,
然后根据key,也就是this对象,当前方法调用对象ThreadLocal,
获取它的threadLocalHashCode与上Entry数组长度-1( key.threadLocalHashCode & (len-1)),得到索引值 set方法往索引位置设置Entry对象,获取修改Entry对象的value值
get方法得到索引位置Entry对象,并获取Entry对象的value值

之所以Entry是一个弱引用,是因为如果所以当外部强引用接触时,可以保证Entry数组的引用,不会影响到ThreadLocal对象的回收

JDK动态代理

//接口
public interface DoWork {
    void work();
}

//实现类
public class Coding implements DoWork{
    @Override
    public void work() {
        System.out.println("madaima....");
    }
}

//处理类
public class MyHandler implements InvocationHandler {
    private Object object;

    public MyHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before....");
        method.invoke(object,args);
        System.out.println("after....");
        return object;
    }
}

//测试类
public class JdkProxyDemo {
    public static void main(String[] args) {
        DoWork o = (DoWork) Proxy.newProxyInstance(Coding.class.getClassLoader(), new Class[]{DoWork.class}, new MyHandler(new Coding()));
        o.work();
    }
}

//输出结果
before....
madaima....
after....
源码分析:

顺便梳理了一下JDK动态代理的源码,部分特殊处理代码已经删除,也做了详细注释。如果只想了解弱引用,可以直接略过源码看总结。

public class Proxy {
    //静态常量map,用构造方法实例化
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    //入口
    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
        //得到Class对象
        Class<?> cl = getProxyClass0(loader, intfs);
        //获取构造方法
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        //反射生成对象(就是在MyHandler类中h作为参数传入构造方法)
        return cons.newInstance(new Object[]{h});
    }

    private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
        return proxyClassCache.get(loader, interfaces);
    }
    
    //WeakCache key的生成方法
    private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }
    }

    //WeakCache value的生成方法
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        private static final String proxyClassNamePrefix = "$Proxy";

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
           //这里本来有一段验证interfaces是否正确的逻辑,验证成功放入interfaceSet

            String proxyPkg = null;
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE("com.sun.proxy") + ".";
            }
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix("$Proxy") + num;
            //生成字节码
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            //生成Class对象
            return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
        }
    }
}

class WeakCache<K, P, V> {
    //ClassLoader生成的弱引用CacheKey为key,
    //subKey是用interfaces组成的类,也是个弱引用(详情见KeyFactory类)
    //value是一个方法(详见Factory类)
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    //上面这个map的subkey的生成方法
    //Proxy的静态常量proxyClassCache,new的时候已经作为构造方法参数完成赋值
    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;
    
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
    
    //key:ClassLoader  ClassLoader:Class<?>... interfaces
    public V get(K key, P parameter) {
        //生成CacheKey
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        //尝试去获取value值,不存在的情况下放入一个空的HashMap
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        //用subKeyFactory生成一个subKey
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            //如果supplier方法已经存在,用他生成value,直接返回即可
            if (supplier != null) {
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            //懒加载Factory
            if (factory == null) {
                //做了一些判断,其实就是常量valueFactory(ProxyClassFactory)
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                //如果value的生成方法为null,那就放一个进去,也就是factory
                supplier = valuesMap.putIfAbsent(subKey, factory);
                //这里主要是预发布别的线程提前put,那就把别的线程put进去的factory直接拿来用
                if (supplier == null) {
                    supplier = factory;
                }
            } else {
                //这里不用看了,一些特殊情况的处理
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
}

总结:WeakCache的get方法最终目就是从静态常量map(WeakCache)得到ProxyClassFactory,并生成的一个value,就是代理生成的Class对象。
map的key和subKey都是弱引用,原因在于如果不使用弱引用,静态常量map的引用将一直存在,ClassLoader和interfaces对象将永远得不到垃圾回收。弱引用的优势在于下一次GC就会进行回收该对象。

4. 虚引用

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,用 PhantomReference 类来表示,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。
PhantomReference设计的目的就是可以在对象被回收之前收到通知(通过注册的队列ReferenceQueue)

public class PhantomReference<T> extends Reference<T> {
    public T get() {
        return null;
    }
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}