Java通过强引用、软引用、弱引用、虚弱引用四种引用类型来方便管理对象的生命周期。
PhantomReference,Abstract Reference,ReferenceQueue,SoftReference,WeakReference
ReferenceQueue:单链表结构,存储被持有回收对象的引用类型,可以和四种引用类型组合使用,而虚引用则必须和ReferenceQueue关联使用。
Reference供描述所有引用对象通用的行为的抽象类
1. Strong reference 强引用
实际编码中最常见的一种引用类型。常见形式如:A a = new A();等。强引用本身存储在栈内存中,其存储指向对内存中对象的地址。一般情况下,当对内存中的对象不在有任何强引用指向它时,垃圾回收器可能对此内存进行垃圾回收。如:a = null,此时,刚刚在堆中分配地址并新建的a对象没有其它的任何引用,当系统进行垃圾回收时,堆内存将被垃圾回收。
SoftReference、WeakReference、PhantomReference都是类java.lang.ref.Reference的子类。Reference作为抽象基类,定义了其子类对象的基本操作。Reference子类都具有如下特点:
a.Reference子类不能无参化直接创建,必须至少以强引用对象为构造参数,创建各自的子类对象;
b.因为1中以强引用对象为构造参数创建对象,因此,使得原本强引用所指向的堆内存中的对象将不再只与强引用本身直接关联,与Reference的子类对象的引用也有一定联系。且此种联系可能影响到对象的垃圾回收。
根据不同的子类对象对其指示对象(强引用所指向的堆内存中的对象)的垃圾回收不同的影响特点,分别形成了三个子类,分别形成了三个子类,即SoftReference、WeakReference和PhantomReference。
2.Soft Reference 软引用
软引用的一般使用形式如下:
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);
通过对象的强引用为参数,创建了一个SoftReference对象,并使栈内存中的srA指向此对象。
此时,进行如下编码:a = null,对于原本a所指向的A对象的垃圾回收有什么影响呢?
import java.lang.ref.SoftReference;
public class ReferenceTest{
public static void main(){
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);
a = null;
if (srA.get() == null){
System.out.println("a对象进入垃圾回收流程");
} else {
System.out.println("a对象尚未被回收" + srA.get());
}
System.gc();
if (srA.get() == null){
System.out.println("a对象进入垃圾回收流程");
} else {
System.out.println("a对象尚未被回收" + srA.get());
}
}
}
class A{
}
输出结果为:
a对象尚未被回收com.twinflag.iisweb.A@33833882
a对象尚未被回收com.twinflag.iisweb.A@33833882
当a = null后,堆内存中的A对象将不再有任何的强引用指向它,但此时尚存在srA引用的对象指向A对象。当第一次调用srA.get()方法返回此指示对象时,由于垃圾回收器很有可能尚未进行垃圾回收,此时get()是有结果的。当程序执行System.gc()强制垃圾回收后,发现A对象依然未被垃圾回收。那么,软引用所指示的对象什么时候才开始被垃圾回收呢?需要满足如下两个条件:
1.当其指示的对象没有任何强引用对象指向它;
2.当虚拟内存不足时。
因此,SoftReference变相的延长了其指示对象占据堆内存的时间,知道虚拟机内存不足时回收器才回收此堆内存空间。
3.Weak Reference 弱引用
同样的,弱引用的一般使用形式如下:
A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);
当没有任何强引用指向此对象时,其垃圾回收又具有什么特性呢?
import java.lang.ref.SoftReference;
public class ReferenceTest{
public static void main(){
A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);
a = null;
if (wrA.get() == null){
System.out.println("a对象进入垃圾回收流程");
} else {
System.out.println("a对象尚未被回收" + wrA.get());
}
System.gc();
if (wrA.get() == null){
System.out.println("a对象进入垃圾回收流程");
} else {
System.out.println("a对象尚未被回收" + wrA.get());
}
}
}
class A{
}
输出结果为:
a对象尚未被回收com.twinflag.iisweb.A@33833882
a对象进入垃圾回收流程
输出的第一条结果解释同上。当进行垃圾回收后,wrA.get()将返回null,表名其指示对象进入到了垃圾回收过程中。因此,对弱引用特点总结为:
WeakReference不改变原有强引用对象的垃圾回收时机,一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。那么依据此特点,很可能有疑问:WeakReference存在又有什么意义呢?
其主要使用场景见于:当前已有强引用指向强引用对象,此时由于业务场景需要,需要增加对此对象的引用,同时又不希望改变此引用的垃圾回收时机,此时WeakReference正好符合需求,每次GC都会回收弱引用对象。
WeakReference第二种使用方式:WeakReference(T referent, ReferenceQueue<? super T> q)
这个构造方法多了个参数,是个引用队列类型;当发生GC后,被弱引用指向的对象被回收,同时这些弱引用将会被添加到这个引用队列当中。
使用场景:直接看ThreadLocal中的map实现,此map继承了弱引用的WeakReference,防止map中的key引用的对象无法被回收;除了ThreadLocal,还有一些高速缓存场景;因为缓存数据用来加快执行速度,但是又不能无限制的将数据存入缓存,因为内存容量有限,这时可以使用弱引用,GC时及时清理缓存对象。
4.PhantomReference
与SoftReference或WeakReference相比,PhantomReference主要差别体现在如下几点:
1.PhantomReference只有一个构造函数PhantomReference(T referent, ReferenceQueue<? super T> q),因此,PhantomReference使用必须结合ReferenceQueue;
2.不管有无强引用指向PhantomReference的指示对象,PhantomReference的get()方法返回结果都是null。
public class ReferenceTest{
public static void main(){
A a = new A();
ReferenceQueue<A> rq = new ReferenceQueue<A>();
PhantomReference<A> prA = new PhantomReference<A>(a, rq);
System.out.println("prA.get():" + prA.get());
a = null;
System.gc();
try{
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("rq item:" + rq.poll());
}
}
class A{
}
输出结果为:
prA.get():null
rq item:java.lang.ref.PhantomReference@33833882