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的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);
}
}