一文读懂java中的Reference和引用类型

一文读懂java中的Reference和引用类型java

简介

java中有值类型也有引用类型,引用类型通常是针对于java中对象来讲的,今天介绍一下java中的引用类型。java为引用类型专门定义了一个类叫作Reference。Reference是跟java垃圾回收机制息息相关的类,经过探讨Reference的实现能够更加深刻的理解java的垃圾回收是怎么工做的。git

本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。程序员

java中的四种引用类型分别是:强引用,软引用,弱引用和虚引用。github

强引用Strong Reference

java中的引用默认就是强引用,任何一个对象的赋值操做就产生了对这个对象的强引用。函数

咱们看一个例子:this

public class StrongReferenceUsage {

    @Test
    public void stringReference(){
        Object obj = new Object();
    }
}
复制代码

上面咱们new了一个Object对象,并将其赋值给obj,这个obj就是new Object()的强引用。spa

强引用的特性是只要有强引用存在,被引用的对象就不会被垃圾回收。3d

软引用Soft Reference

软引用在java中有个专门的SoftReference类型,软引用的意思是只有在内存不足的状况下,被引用的对象才会被回收。code

先看下SoftReference的定义:cdn

public class SoftReference<T> extends Reference<T> 复制代码

SoftReference继承自Reference。它有两种构造函数:

public SoftReference(T referent) 复制代码

和:

public SoftReference(T referent, ReferenceQueue<? super T> q) 复制代码

第一个参数很好理解,就是软引用的对象,第二个参数叫作ReferenceQueue,是用来存储封装的待回收Reference对象的,ReferenceQueue中的对象是由Reference类中的ReferenceHandler内部类进行处理的。

咱们举个SoftReference的例子:

@Test
    public void softReference(){
        Object obj = new Object();
        SoftReference<Object> soft = new SoftReference<>(obj);
        obj = null;
        log.info("{}",soft.get());
        System.gc();
        log.info("{}",soft.get());
    }
复制代码

输出结果:

22:50:43.733 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4
22:50:43.749 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4
复制代码

能够看到在内存充足的状况下,SoftReference引用的对象是不会被回收的。

弱引用weak Reference

weakReference和softReference很相似,不一样的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不论是否内存不足。

一样的WeakReference也有两个构造函数:

public WeakReference(T referent)public WeakReference(T referent, ReferenceQueue<? super T> q)复制代码

含义和SoftReference一致,这里就再也不重复表述了。

咱们看下弱引用的例子:

@Test
    public void weakReference() throws InterruptedException {
        Object obj = new Object();
        WeakReference<Object> weak = new WeakReference<>(obj);
        obj = null;
        log.info("{}",weak.get());
        System.gc();
        log.info("{}",weak.get());
    }
复制代码

输出结果:

22:58:02.019 [main] INFO com.flydean.WeakReferenceUsage - java.lang.Object@71bc1ae4
22:58:02.047 [main] INFO com.flydean.WeakReferenceUsage - null
复制代码

咱们看到gc事后,弱引用的对象被回收掉了。

虚引用PhantomReference

PhantomReference的做用是跟踪垃圾回收器收集对象的活动,在GC的过程当中,若是发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员本身处理,当程序员调用ReferenceQueue.pull()方法,将引用出ReferenceQueue移除以后,Reference对象会变成Inactive状态,意味着被引用的对象能够被回收了。

和SoftReference和WeakReference不一样的是,PhantomReference只有一个构造函数,必须传入ReferenceQueue:

public PhantomReference(T referent, ReferenceQueue<? super T> q) 复制代码

看一个PhantomReference的例子:

@Slf4j
public class PhantomReferenceUsage {

    @Test
    public void usePhantomReference(){
        ReferenceQueue<Object> rq = new ReferenceQueue<>();
        Object obj = new Object();
        PhantomReference<Object> phantomReference = new PhantomReference<>(obj,rq);
        obj = null;
        log.info("{}",phantomReference.get());
        System.gc();
        Reference<Object> r = (Reference<Object>)rq.poll();
        log.info("{}",r);
    }
}
复制代码

运行结果:

07:06:46.336 [main] INFO com.flydean.PhantomReferenceUsage - null
07:06:46.353 [main] INFO com.flydean.PhantomReferenceUsage - java.lang.ref.PhantomReference@136432db
复制代码

咱们看到get的值是null,而GC事后,poll是有值的。

由于PhantomReference引用的是须要被垃圾回收的对象,因此在类的定义中,get一直都是返回null:

public T get() {
        return null;
    }
复制代码

Reference和ReferenceQueue

讲完上面的四种引用,接下来咱们谈一下他们的父类Reference和ReferenceQueue的做用。

Reference是一个抽象类,每一个Reference都有一个指向的对象,在Reference中有5个很是重要的属性:referent,next,discovered,pending,queue。

private T referent;         /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
Reference next;
transient private Reference<T> discovered;  /* used by VM */
private static Reference<Object> pending = null;
复制代码

每一个Reference均可以当作是一个节点,多个Reference经过next,discovered和pending这三个属性进行关联。

先用一张图来对Reference有个总体的概念:

referent就是Reference实际引用的对象。

经过next属性,能够构建ReferenceQueue。

经过discovered属性,能够构建Discovered List。

经过pending属性,能够构建Pending List。

四大状态

在讲这三个Queue/List以前,咱们先讲一下Reference的四个状态:

从上面的图中,咱们能够看到一个Reference能够有四个状态。

由于Reference有两个构造函数,一个带ReferenceQueue,一个不带。

Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
复制代码

对于带ReferenceQueue的Reference,GC会把要回收对象的Reference放到ReferenceQueue中,后续该Reference须要程序员本身处理(调用poll方法)。

不带ReferenceQueue的Reference,由GC本身处理,待回收的对象其Reference状态会变成Inactive。

建立好了Reference,就进入active状态。

active状态下,若是引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。

Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。

Pending状态表明等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中。

入Queue的对象,其状态就变成了Enqueued。

Enqueued状态的对象,若是调用poll方法从ReferenceQueue拿出,则该Reference的状态就变成了Inactive,等待GC的回收。

这就是Reference的一个完整的生命周期。

三个Queue/List

有了上面四个状态的概念,咱们接下来说三个Queue/List:ReferenceQueue,discovered List和pending List。

ReferenceQueue在讲状态的时候已经讲过了,它本质是由Reference中的next链接而成的。用来存储GC待回收的对象。

pending List就是待入ReferenceQueue的list。

discovered List这个有点特别,在Pending状态时候,discovered List就等于pending List。

在Active状态的时候,discovered List实际上维持的是一个引用链。经过这个引用链,咱们能够得到引用的链式结构,当某个Reference状态再也不是Active状态时,须要将这个Reference从discovered List中删除。

WeakHashMap

最后讲一下WeakHashMap,WeakHashMap跟WeakReference有点相似,在WeakHashMap若是key再也不被使用,被赋值为null的时候,该key对应的Entry会自动从WeakHashMap中删除。

咱们举个例子:

@Test
    public void useWeakHashMap(){
        WeakHashMap<Object, Object> map = new WeakHashMap<>();
        Object key1= new Object();
        Object value1= new Object();
        Object key2= new Object();
        Object value2= new Object();

        map.put(key1, value1);
        map.put(key2, value2);
        log.info("{}",map);

        key1 = null;
        System.gc();
        log.info("{}",map);

    }
复制代码

输出结果:

[main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc, java.lang.Object@11028347=java.lang.Object@1f89ab83}
[main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc}
复制代码

能够看到gc事后,WeakHashMap只有一个Entry了。

总结

本文讲解了4个java中的引用类型,并深刻探讨了Reference的内部机制,感兴趣的小伙伴能够留言一块儿讨论。

本文的例子github.com/ddean2009/l…

欢迎关注个人公众号:程序那些事,更多精彩等着您! 更多内容请访问 www.flydean.com

相关文章
相关标签/搜索