文章目录
1 四种引用类型
Java将引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种类型,每种引用强度依次逐渐减弱。
1.1 强引用
强引用即普遍意义上的引用,比如:Object o = new Object(),这里o就是一个强引用,只要这个引用关系存在,对象永远不会被回收,即使内存不足,JVM只会抛出OOM异常。
1.2 软引用
软引用用来指向一些后续可能会使用到但并非必须存在的对象,比如创建一个软引用:new SoftReference<>(new ArrayList<>()),这个软引用指向一个ArrayList对象。在系统将要发生OOM异常前,会把这个ArrayList对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出OOM异常。
根据软引用这个特性,可将其使用于缓存的场景。
1.3 弱引用
弱引用WeakReference和软引用类似,也是用来指向那些非必须的对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用指向的对象。
弱引用在JDK中典型的应用有ThreadLocal。
1.4 虚引用
虚引用PhantomReference和弱引用一样也是发现即回收,其一般是用来管理堆外内存(直接内存)。
2 ThreadLocal
2.1 ThreadLocal源码解读
public class TestThreadLocal {
public static void main(String[] args) throws InterruptedException {
ThreadLocal<String> local = new ThreadLocal<>();
new Thread(() -> local.set("Kitty")).start();
Thread.sleep(2000);
new Thread(() -> System.out.println(local.get())).start();
}
}
输出:
null
上面的代码中,第二个线程并没有输出Kitty而是输出了null。通过查看ThreadLocal.set()方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看到ThreadLocal的set方法,是将value保存到当前线程中的一个key-value存储结构ThreadLocalMap中,key为ThreadLocal对象,value即set的值。所以一个线程调用ThreadLocal的set方法设置值之后,其他的线程通过get方法取不到值是理所当然的。以下是ThreadLocal在Thread中的示意图:
2.2 ThreadLocal之内存泄漏
为什么ThreadLocal的Entry是弱引用?因为如果是强引用,即使ThreadLocal local = null,但key依然指向ThreadLocal对象,ThreadLocal对象不会被回收,导致内存泄漏。而弱引用,ThreadLocal对象会被回收掉。但是,ThreadLocal对象被回收掉,key = null,value永远不会被访问到,所以依然存在内存泄漏的问题。解决方法是,使用ThreadLocal.set设置值,使用完之后,要调用remove方法移除。
2.3 ThreadLocal应用场景
ThreadLocal在Spring框架中,用于实现声明式事务。开启事务后,将从连接池中拿到的数据库连接,存放到ThrealLocal中,保证事务中的所有数据库操作都是从ThreadLocal中拿到同一个数据库连接。