一. 简介
WeakReference 如字面意思,弱引用,当一个对象仅仅被weak reference(弱引用)指向, 而没有任何其他strong reference(强引用)指向的时候, 如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。
二. 认识WeakReference类
WeakReference 继承 Reference,其中只有两个构造函数:
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
- WeakReference(T referent):referent就是被弱引用的对象(注意区分弱引用对象和被弱引用的对象,弱引用对象是指WeakReference的实例或者其子类的实例),比如有一个Apple实例apple,可以如下使用,并且通过get()方法来获取apple引用。也可以再创建一个继承WeakReference的类来对Apple进行弱引用,下面就会使用这种方式。
WeakReference<Apple> appleWeakReference = new WeakReference<>(apple);
Apple apple2 = appleWeakReference.get();
- WeakReference(T referent, ReferenceQueue<? super T> q):与上面的构造方法比较,多了个ReferenceQueue,在对象被回收后,会把弱引用对象,也就是WeakReference对象或者其子类的对象,放入队列ReferenceQueue中,注意不是被弱引用的对象,被弱引用的对象已经被回收了。
三. 使用WeakReference
下面是使用继承WeakReference的方式来使用弱引用,并且不使用ReferenceQueue。
简单类Apple
package io.github.brightloong.lab.reference;
public class Apple {
private String name;
public Apple(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 覆盖finalize,在回收的时候会执行。
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Apple: " + name + " finalize。");
}
@Override
public String toString() {
return "Apple{" +
"name='" + name + '\'' +
'}' + ", hashCode:" + this.hashCode();
}
}
继承WeakReference的Salad
import java.lang.ref.WeakReference;
/**
* Salad class
* 继承WeakReference,将Apple作为弱引用。
* 注意到时候回收的是Apple,而不是Salad
*/
public class Salad extends WeakReference<Apple> {
public Salad(Apple apple) {
super(apple);
}
}
Clent调用和输出
import java.lang.ref.WeakReference;
public class Client {
public static void main(String[] args) {
Salad salad = new Salad(new Apple("红富士"));
//通过WeakReference的get()方法获取Apple
System.out.println("Apple:" + salad.get());
System.gc();
try {
//休眠一下,在运行的时候加上虚拟机参数-XX:+PrintGCDetails,输出gc信息,确定gc发生了。
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果为空,代表被回收了
if (salad.get() == null) {
System.out.println("clear Apple。");
}
}
}
输出如下:
Apple:Apple{name='红富士'}, hashCode:474675244
Apple:红富士 finalize。
clear Apple。
ReferenceQueue的使用
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class Client2 {
public static void main(String[] args) {
ReferenceQueue<Apple> appleReferenceQueue = new ReferenceQueue<>();
WeakReference<Apple> appleWeakReference = new WeakReference<Apple>(new Apple("青苹果"), appleReferenceQueue);
WeakReference<Apple> appleWeakReference2 = new WeakReference<Apple>(new Apple("毒苹果"), appleReferenceQueue);
System.out.println("=====gc调用前=====");
Reference<? extends Apple> reference = null;
while ((reference = appleReferenceQueue.poll()) != null ) {
//不会输出,因为没有回收被弱引用的对象,并不会加入队列中
System.out.println(reference);
}
System.out.println(appleWeakReference);
System.out.println(appleWeakReference2);
System.out.println(appleWeakReference.get());
System.out.println(appleWeakReference2.get());
System.out.println("=====调用gc=====");
System.gc();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====gc调用后=====");
//下面两个输出为null,表示对象被回收了
System.out.println(appleWeakReference.get());
System.out.println(appleWeakReference2.get());
//输出结果,并且就是上面的appleWeakReference、appleWeakReference2,再次证明对象被回收了
Reference<? extends Apple> reference2 = null;
while ((reference2 = appleReferenceQueue.poll()) != null ) {
//如果使用继承的方式就可以包含其他信息了
System.out.println("appleReferenceQueue中:" + reference2);
}
}
}
结果输出如下:
=====gc调用前=====
java.lang.ref.WeakReference@5e91993f
java.lang.ref.WeakReference@1c4af82c
Apple{name='青苹果'}, hashCode:932583850
Apple{name='毒苹果'}, hashCode:212628335
=====调用gc=====
Apple:毒苹果 finalize。
Apple:青苹果 finalize。
=====gc调用后=====
null
null
appleReferenceQueue中:java.lang.ref.WeakReference@1c4af82c
appleReferenceQueue中:java.lang.ref.WeakReference@5e91993f
可以看到在队列中(ReferenceQueue),调用gc之前是没有内容的,调用gc之后,对象被回收了,并且弱引用对象appleWeakReference和appleWeakReference2被放入了队列中。
关于其他三种引用,强引用、软引用、虚引用,可以参考:
Carson带你学Java:深入解析引用类型-强、软、弱、虚