java中四种引用:
a.强引用
平常我们代码中写到的引用类型都是强引用类型,比如Object obj = new Object();, Object实例就有一个强引用类型指向它,在GC过程中即使发生OOM,该Object实例都不会被回收。
b. 软引用 - SoftReference
定义方式:SoftReference sr = new SoftReference(new Object()); 一个对象的实例被一个软引用实例指向,那么在GC过程中发生OOM之前,该Object对象实例会被回收掉,在内存充足的情况下是不会被回收的。同时可以将一个引用队列关联到该软引用上,在软引用指向的对象被回收后,该软引用会被加入到关联的引用队列中。我们可以通过Reference的get()方法获取到该软引用指向的对象实例。
c. 弱引用 - WeakReference
弱引用基本上同上面的软引用类似,WeakReference wr = new WeakReference(new Object());,但是特殊点就是在它被创建后的下一次GC时候其指向的对象实例会被回收掉,不管内存是不是充足,反正就是活不过一次GC。JDK中的WeakHashMap就是使用到WeakReference,其Key就是被包装成WeakReference。
d. 虚引用 - PhantomReference
定义方式:PhantomReference pr = new PhantomReference(new Object(), new ReferenceQueue()),虚引用对象再被定义时,必须指定一个引用队列实例。JDK文档中介绍它主要用于对象被回收前资源的释放操作,替换finalize()方法。它和前面的两个软引用和弱引用不同的地方有两点:
在看ThreadLocal源码的时候,其中嵌套类ThreadLocalMap中的Entry继承了WeakReferenc,为了能搞清楚ThreadLocal,只能先了解下了WeakReferenc,以及其他的三种引用。
主要先谈下弱引用
WeakReference如字面意思,弱引用, 当一个对象仅仅被weak reference(弱引用)指向, 而没有任何其他strong reference(强引用)指向的时候, 如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。
首先先来看下WeakReference中的构造函数
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> queue) {
super(referent, queue);
}
}
WeakReference(T referent)
referent就是被弱引用的对象(注意区分弱引用对象和被弱引用的对应,弱引用对象是指WeakReference的实例或者其子类的实例),比如有一个Apple实例apple,可以如下使用,并且通过get()方法来获取apple引用。也可以再创建一个继承WeakReference的类来对Apple进行弱引用,下面就会使用这种方式。
WeakReference<Car> appleWeakReference = new WeakReference<>(car);
Car car1 = appleWeakReference.get();
public class Test {
public static void main(String[] args) {
Audi audi = new Audi(new Car("奥迪"));
System.out.println(audi.get());
System.gc();
try{
Thread.sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
//检查是否为空被回收
if (audi.get() == null){
System.out.println("已经被清除");
}
}
}
输出如下:
Car{name='奥迪'}
[GC (System.gc()) [PSYoungGen: 3084K->696K(35840K)] 3084K->704K(117760K), 0.0186270 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
[Full GC (System.gc()) [PSYoungGen: 696K->0K(35840K)] [ParOldGen: 8K->618K(81920K)] 704K->618K(117760K), [Metaspace: 3225K->3225K(1056768K)], 0.0250364 secs] [Times: user=0.02 sys=0.00, real=0.03 secs]
Car: 奥迪finalize
已经被清除
- WeakReference(T referent, ReferenceQueue<? super T> queue)
与上面的构造方法比较,多了个ReferenceQueue,在对象被回收后,会把弱引用对象,也就是WeakReference对象或者其子类的对象,放入队列ReferenceQueue中,注意不是被弱引用的对象,被弱引用的对象已经被回收了。
public class Test2 {
public static void main(String[] args) {
ReferenceQueue<Car> referenceQueue = new ReferenceQueue<>();
WeakReference<Car> carWeakReference =new WeakReference<Car>(new Car("奥迪"),referenceQueue);
WeakReference<Car> carWeakReference1 = new WeakReference<Car>(new Car("奔驰"),referenceQueue);
System.out.println("=====调用GC前=====");
Reference<? extends Car> reference = null;
while ((reference = referenceQueue.poll()) != null){
System.out.println(reference);
}
System.out.println(carWeakReference);
System.out.println(carWeakReference1);
System.out.println(carWeakReference.get());
System.out.println(carWeakReference1.get());
System.out.println("=====调用GC===");
System.gc();
try{
Thread.sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(carWeakReference.get());
System.out.println(carWeakReference1.get());
Reference<? extends Car> reference1 = null;
while((reference1 = referenceQueue.poll()) != null){
System.out.println("referenceQueue:"+reference1);
}
}
}
输出:
=====调用GC前=====
java.lang.ref.WeakReference@74a14482
java.lang.ref.WeakReference@1540e19d
Car{name='奥迪'}
Car{name='奔驰'}
=====调用GC===
Car: 奔驰finalize
Car: 奥迪finalize
null
null
referenceQueue:java.lang.ref.WeakReference@74a14482
referenceQueue:java.lang.ref.WeakReference@1540e19d
可以看到在队列中(ReferenceQueue),调用gc之前是没有内容的,调用gc之后,对象被回收了,并且弱引用对象appleWeakReference和appleWeakReference2被放入了队列中。