前言
我们在学习java的时候会学习堆栈的相关知识。我们知道,在我们声明一个对象变量的时候,他的声明变量(引用)保存在栈上面,而对象保存在堆上面。引用的不可达会有两种方式:一是我们手动设置为null;二是方法执行完出栈。此时的对象在没有引用的情况下,会在发生GC时被回收。那我们常用的这种引用类型,就称之为强引用。
在JDK1.2之后,java对引用的概念进行了扩充。将引用分为四类:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。我们平时的开发工作中很少用到除了强引用之外的其他引用,但是其他的引用类型我们也必须要了解。
强引用(Strong Reference)
概念
最普遍的引用:
Object obj1 = new Object();
Object obj2 = new Object();
Object obj3 = new Object();
Object obj4 = new Object();
Object obj5 = new Object();
Object obj6 = new Object();
特点
在抛出OutOfMemoryError的时候也不会被回收。想要回收,可以手动设置为null来弱化引用或者方法出栈自动弱化。
应用场景
在此不赘述,我们日常中的变量声明等等均为强引用。
软引用(Soft Reference)
概念
软引用是一种比强引用强度低一级的引用:
String s = "Hello World!";
SoftReference<String> sf = new SoftReference<>(s);
特点
只有当内存空间不足的时候,GC才会回收该对象。
应用场景
主要应用在高速缓存场景等等。
弱引用(Weak Reference)
概念
弱引用是一种比软引用强度低一级的引用:
String s = "Hello World!";
WeakReference<String> sf = new WeakReference<>(s);
特点
发生GC时,会回收该对象。
应用场景
主要应用在高速缓存场景等等。此种引用也多数用来解决防止内存溢出的问题。
补充
我们熟悉的ThresadLocal类的Entry内部类,则使用的弱引用来弱化引用,从而实现线城池的情况下不会内存溢出。
我们使用ThreadLocal来隔离线程之间的资源,从而实现线程安全,它的实现原理是什么呢?
ThreadLocal实现原理
我们先从线程类看起,我们的线程类内部有一个类型为ThreadLocalMap的成员变量,如下图所示:
再来看看ThreadLocal的内部类ThreadLocalMap:
由以上两张图片,我们可以看出,ThreadLocalMap的内部类Entry就是一个弱引用,而且是对ThreadLocal对象的弱引用。那么,这么设计的原因是什么呢?我们带着这个疑问,来看一下ThreadLocal的get和set方法:
我们先看一下get方法,逻辑也比较清晰:先获取当前线程的ThreadLocalMap属性对象,从这个属性对象中获取值,获取值的时候,key就是当前ThreadLocal实体。其中两个判空比较关键,如果map==null,就说明该线程没有初始化过ThreadLocalMap属性,那么就返回默认的初始值。这个初始值我们翻阅源码就知道是一个null,而且获取初始值的方法是一个被protected关键字表示的方法,表示可以由子类继承并重写。
再来看一下set方法,逻辑也比较清晰:先获取到当前线程的ThreadLocalMap属性对象,如果这个属性对象是null,则初始化一个属性对象并赋值,赋值的过程就是给Entry这个对象添加对于当前ThreadLocal对象的弱引用。
由以上逻辑我们可以看出,ThreadLocal实现线程隔离的内部逻辑,就是把自己作为key,保存在各个线程对应的ThreadLocalMap中的Entry中。在get的时候,是获取当前线程,并把自己作为key,来获取当前线程对象内部的ThreadLocalMap属性的Entry的value来获取我们设置的值。
我们想一下,如果Entry没有使用弱引用类型,而是强引用类型,会怎么样呢?如果这样的话,我们在手动设置ThreadLocal变量为null的时候,ThreadLocal对象不回被回收,因为还有线程内部属性的Entry内部类的key的一个强引用,这样的话,ThreadLocal的回收会随着线程的销毁而之后被回收。但是,如果我们使用线程池的话,线程不会被销毁,那么ThreadLocal就会一直不被回收。
所以,ThreadLocalMap里面的Entry使用了弱引用的对象类型,来保证ThreadLocal没有强引用的时候(也就是我们期望他被回收的时候)被GC回收。
我们在日常开发过程中,ThreadLocal对象一般会设置为public static或者private static,所以我们不用关心回收的问题,但是这个例子就是一个很好的弱引用的应用示例。
虚引用(Phantom Reference)
概念
虚引用是一种比弱引用强度低一级的引用,它必须结合引用队列来使用:
String s = "Hello World!";
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
PhantomReference phantomReference = new PhantomReference(s, referenceQueue);
特点
发生GC时,会回收该对象,并且将引用放入引用队列
应用场景
主要应用我们要监控某个对象的生命周期时。
文章评论