Reference。Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。
Java中存在四种引用:
- StrongReference(强引用)
- SoftReferenc(软引用)
- WeakReferenc(弱引用)
- PhantomReference(虚引用)
引用的结构图如下:
ReferenceQueue引用队列是为了配合SoftReference、WeakReference、PhantomReference使用,它们三个在GC回收之前会被放到引用队列里ReferenceQueue保存下。
1、StrongReference(强引用)
java中的引用默认就是强引用,任何一个对象的赋值操作就产生了对这个对象的强引用。
如:Object obj = new Object();
我们new了一个Object对象,并将其赋值给obj,这个obj就是new Object()的强引用。
强引用的特性是只要有强引用存在,被引用的对象就不会被垃圾回收。
2、SoftReferenc(软引用)
SoftReference所提供的功能,意思是只有在内存不足的情况下,被引用的对象才会被回收。
SoftReference源码如下:
package java.lang.ref;
public class SoftReference<T> extends Reference<T> {
// 时间锁,由垃圾收集器更新。
static private long clock;
// 每次调用get方法都会更新该时间戳。JVM可能会在选择要清除的软引用时使用该字段,
// 但这不是强制必须的。
private long timestamp;
// 返回对象的引用。如果该引用对象已经被程序或者垃圾收集器清除,则返回null。
// 把最近一次垃圾回收时间赋值给timestamp
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
}
测试代码:
@Slf4j
public class RefernceTest {
public static void main(String[] args) {
SoftReference<Object> soft = new SoftReference<>(new Object());
log.info("{}",soft.get());
System.gc();
log.info("{}",soft.get());
}
}
// 输出结果:
17:26:11.015 [main] INFO com.example.demo.thread.RefernceTest - java.lang.Object@68f7aae2
17:26:11.029 [main] INFO com.example.demo.thread.RefernceTest - java.lang.Object@68f7aae2
3、WeakReferenc(弱引用)
WeekReference所提供的功能,不同的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足。
WeekReference源码如下:
package java.lang.ref;
public class WeakReference<T> extends Reference<T> {
// 创建没有注册ReferenceQueue的弱引用
public WeakReference(T referent) {
super(referent);
}
// 创建注册了ReferenceQueue的弱引用
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
@Slf4j
public class RefernceTest {
public static void main(String[] args) {
WeakReference<Object> weak = new WeakReference<>(new Object());
log.info("{}",weak.get());
System.gc();
log.info("{}",weak.get());
}
}
// 输出结果如下
17:31:40.979 [main] INFO com.example.demo.thread.RefernceTest - java.lang.Object@68f7aae2
17:31:40.998 [main] INFO com.example.demo.thread.RefernceTest - null
jdk中的弱引用代码示例:
public static void main(String[] args) {
ThreadLocal<Object> tl = new ThreadLocal<Object>();
Object obj = new Object();
tl.set(obj);
tl.remove();
}
ThreadLocal中放入obj对象,key值为 创建的tl对象。 在set时,内部将使用Entry存储,Entry是弱引用,以保证在t1没有强引用的情况下,可以直接gc回收;但同时也存在一个问题,当前线程中存的obj对象的key回收后为null了,但数据还在,所以在最后需要手动的 remove,以防止内存泄露。
4、虚引用PhantomReference
PhantomReference所提供的关联功能。
作用是:跟踪垃圾回收器收集对象的活动,在GC的过程中,如果发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员自己处理,当程序员调用ReferenceQueue.pull()方法,将引用从ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。
可以用來管理堆外不存:有些java对象关联着不归jvm管理的堆外不存,这些java对象回收时,关联的堆外内存也应该回收,回收方式为 在java对象上加虚引用,因为虚引用被回收后,会被加入到queue队列中,gc线程监控着对列,队列中有对象,则进行特殊处理。将清理掉对象关联的堆外内存。
PhantomReference和SoftReference和WeakReference不同的是,PhantomReference只有一个构造函数,必须传入ReferenceQueue。
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
代码示例:
@Slf4j
public class RefernceTest {
public static void main(String[] args) {
Object obj = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj,referenceQueue);
log.info("{}","***************GC回收前***************");
log.info("{}",obj);
log.info("{}",phantomReference.get());
log.info("{}",referenceQueue.poll());
log.info("{}","***************启动GC***************");
obj = null;
System.gc();
try {
Thread.sleep(500); //确保GC都执行完了
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("{}",obj);
log.info("{}",phantomReference.get());
log.info("{}",referenceQueue.poll());
}
}
执行结果:
09:59:57.128 [main] INFO com.example.demo.thread.RefernceTest - ***************GC回收前***************
09:59:57.132 [main] INFO com.example.demo.thread.RefernceTest - java.lang.Object@1996cd68
09:59:57.132 [main] INFO com.example.demo.thread.RefernceTest - null
09:59:57.133 [main] INFO com.example.demo.thread.RefernceTest - null
09:59:57.133 [main] INFO com.example.demo.thread.RefernceTest - ***************启动GC***************
09:59:57.647 [main] INFO com.example.demo.thread.RefernceTest - null
09:59:57.647 [main] INFO com.example.demo.thread.RefernceTest - null
09:59:57.647 [main] INFO com.example.demo.thread.RefernceTest - java.lang.ref.PhantomReference@3339ad8e