java为引用类型专门定义了一个类叫做Reference。Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。
Java中存在四种引用:
引用的结构图如下:
ReferenceQueue引用队列是为了配合SoftReference、WeakReference、PhantomReference使用,它们三个在GC回收之前会被放到引用队列里ReferenceQueue保存下。
java中的引用默认就是强引用,任何一个对象的赋值操作就产生了对这个对象的强引用。
如:Object obj = new Object();
我们new了一个Object对象,并将其赋值给obj,这个obj就是new Object()的强引用。
强引用的特性是只要有强引用存在,被引用的对象就不会被垃圾回收。
软引用是由java.lang.ref.SoftReference所提供的功能,意思是只有在内存不足的情况下,被引用的对象才会被回收。
SoftReference源码如下:
- package java.lang.ref;
-
- public class SoftReference
extends Reference { -
- // 时间锁,由垃圾收集器更新。
- 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
- 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
弱引用是由java.lang.ref.WeekReference所提供的功能,不同的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足。
WeekReference源码如下:
- package java.lang.ref;
-
- public class WeakReference
extends Reference { -
- // 创建没有注册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
- 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 obj = new Object();
-
- tl.set(obj);
- tl.remove();
- }
当前线程的ThreadLocal中放入obj对象,key值为 创建的tl对象。 在set时,内部将使用Entry存储,Entry是弱引用,以保证在t1没有强引用的情况下,可以直接gc回收;但同时也存在一个问题,当前线程中存的obj对象的key回收后为null了,但数据还在,所以在最后需要手动的 remove,以防止内存泄露。

虚引用是由java.lang.ref.PhantomReference所提供的关联功能。
作用是:跟踪垃圾回收器收集对象的活动,在GC的过程中,如果发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员自己处理,当程序员调用ReferenceQueue.pull()方法,将引用从ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。
可以用來管理堆外不存:有些java对象关联着不归jvm管理的堆外不存,这些java对象回收时,关联的堆外内存也应该回收,回收方式为 在java对象上加虚引用,因为虚引用被回收后,会被加入到queue队列中,gc线程监控着对列,队列中有对象,则进行特殊处理。将清理掉对象关联的堆外内存。
PhantomReference和SoftReference和WeakReference不同的是,PhantomReference只有一个构造函数,必须传入ReferenceQueue。
- public class PhantomReference
extends Reference { -
- 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
referenceQueue = new ReferenceQueue<>(); - PhantomReference
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