Reference。Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。

Java中存在四种引用:

  1. StrongReference(强引用)
  2. SoftReferenc(软引用)
  3. WeakReferenc(弱引用)
  4. 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中的弱引用代码示例:

Java 内存 基本类型 引用类型 java中都有哪些引用类型_java

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,以防止内存泄露。 

Java 内存 基本类型 引用类型 java中都有哪些引用类型_强引用_02

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