在分析threadLocal源码时,发现有使用弱引用WeakReference,在之前总结GC的时候,也曾简单介绍java引用类型(《jvm GC算法类型》第6节),本文在详细总结下WeakReference的使用方法。
java 4种引用类型:
强:A a=new A(); 此时引用a强引用对象A;不会被GC
软:SoftReference.java,在内存不够时引用对象会被GC;
弱:WeakReference.java,每次GC都会被回收;
虚:PhantomReference.java,每次GC都会被回收;
弱引用指向的对象,每次GC时,对象都会被作为垃圾回收。
看下源码:
public class WeakReference<T> extends Reference<T> {
/**
* 创建一个弱引用,指向给定对象,不使用队列
*
* @param referent 弱引用指向的对象
*/
public WeakReference(T referent) {
super(referent);
}
/**
* 创建一个弱引用,指向给定对象;引用在指定队列中注册
*
* @param referent 弱引用指向的对象
* @param q the 注册弱引用的队列
*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
只有两个构造方法,不废话了,针对中两种构造放方法,直接上使用demo:
1. Demo
先看WeakReference第一种使用方式:
WeakReference(T referent)
public class WeakReferenceDemo {
public static void main(String[] args) {
//强引用
RoleDTO role = new RoleDTO(1, "CEO");
//弱引用
WeakReference<RoleDTO> weakReference = new WeakReference<>(new RoleDTO(1, "CTO"));
//主动让出发GC
System.gc();
try {
//给GC留点时间,保证GC执行完成
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (role == null) {
System.out.println("强引用指向的RoleDTO对象 已经被回收");
}
//weakReference.get()获取弱引用指向的对象,如果对象是null,表示被回收
if (weakReference.get() == null) {
System.out.println("弱引用指向的RoleDTO对象 已经被回收");
}
}
}
//随便创建个对象
import lombok.Data;
@Data
@AllArgsConstructor
public class RoleDTO {
private Integer id;
private String type;
}
输出如下:
上述demo中,创建了两个对象,使用两种引用方式:
- RoleDTO role = new RoleDTO();此role变量属于强引用,GC发生后,它指向的对象不被回收;通过后边打印,已经证明;
- WeakReference< RoleDTO> weakReference= new WeakReference<>(new RoleDTO());此weakReference是弱引用,GC发生后,它指向的对象被回收,通过打印输出已经证明。
下面继续分析WeakReference第二种使用方式:
WeakReference(T referent, ReferenceQueue<? super T> q)
这个构造方法多了个参数,是个引用队列类型;当发生GC后,被弱引用指向的对象被回收,同时这些弱引用将会被添加的这个引用队列当中。
下面看demo:
public class WeakReferenceDemo {
public static void main(String[] args) {
ReferenceQueue<RoleDTO> referenceQueue = new ReferenceQueue<>();
WeakReference<RoleDTO> weakReference = new WeakReference<>(new RoleDTO(3, "CFO"), referenceQueue);
System.out.println("GC执行前----");
Reference<? extends RoleDTO> reference;
while ((reference = referenceQueue.poll()) != null) {
//如果使用继承的方式就可以包含其他信息了
System.out.println("referenceQueue中:" + reference);
}
System.gc();
try {
//给GC留点时间,保证GC执行完成
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("GC执行后----");
if (weakReference.get() == null) {
System.out.println("弱引用指向的RoleDTO对象 已经被回收");
}
Reference<? extends RoleDTO> reference2;
while ((reference2 = referenceQueue.poll()) != null) {
//如果使用继承的方式就可以包含其他信息了
System.out.println("referenceQueue中:" + reference2);
}
}
}
最后输出:
GC执行前----
GC执行后----
弱引用指向的RoleDTO对象 已经被回收
referenceQueue中:java.lang.ref.WeakReference@377dca04
执行前后,我们都打印了引用队列种的内容,GC之前没有打印输出;GC之后,弱引用被放入队列中,被打印输出。
2. 使用场景
- 直接看ThreaLocal中的map实现(请参考一个场景Demo分析ThreadLocal使用方法和原理),此map继承了弱引用WeakReference,防止map中的key引用的对象无法被回收;
- 除了ThreaLocal,还有一些高速缓存场景;因为缓存数据时用来加快执行速度,但是又不能无限制的将数据存入缓存,因为内存容量是有限的,这时可以使用弱引用,GC时及时清理缓存对象。
原文链接:https://blog.csdn.net/csdn_20150804/article/details/103748869