Java引用

四种引用:强引用,软引用,弱引用,虚引用
为什么要引入不同引用的概念?之前由于内存小,对象就两种状态,要么被引用,要么被清除,随着内存的扩大,java充实了对象的状态,于是就有了这四种引用;

强引用

你的代码中绝大多数都是强引用。
如果一个对象,GC root可达,GC清理宁可抛出OOM异常终止程序,也不会清理掉它;

软引用

用于描述那些不是必需的对象,但由于内存充足可保留,适合用作缓存。那么GC什么时候能够清理掉软引用呢?

public class SoftReference<T> extends Reference<T> {

    /**
     * Timestamp clock, updated by the garbage collector
     */
    static private long clock;

    /**
     * Timestamp updated by each invocation of the get method.  The VM may use
     * this field when selecting soft references to be cleared, but it is not
     * required to do so.
     */
    private long timestamp;

    public SoftReference(T referent) {
        super(referent);
        this.timestamp = clock;
    }


    public SoftReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
        this.timestamp = clock;
    }


    public T get() {
        T o = super.get();
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }

}

注意这两个变量clock 和 timestamp;
在JVM发生GC时,也会更新clock的值,意味着clock会记录上次GC发生的时间点;
另一个变量timestamp,在软引用初始化时,会被初始化成clock,同时在get方法被调用时,也会更新timestamp的值;
所以如果一个SoftReference长时间没有被调用(即get方法没被调用)那么clock 和 timestamp之间会形成差值;
首先当GC时,会先检查该SoftReference所引用的对象是否还存活,即GC root是否可达;若不可达就会尝试回收引用对象,这里有几种回收策略,如LRUCurrentHeapPolicy;该种策略下,会对clock 和 timestamp差值与某个值进行比较,这个值的大小与上次GC后剩余空间大小正相关,也就是对空间剩余空间越大,对象存活越长;

弱引用

WeakReference:相对于软引用,它的生命周期更短,当发生GC时,如果扫描到一个对象只有弱引用,不管当前内存是否足够,都会对它进行回收。
还记得ThreadLocal里对它的的引用吗?ThreadLocalMap里的Entry继承的就是WeakReference,它将对ThreadLocal对象的引用设为弱引用,在检测到key为null之后会对entry进行清理再重新rehash,如getEntry,set,remove方法;所以我们一般这样使用:

private final static ThreadLocal memory = new ThreadLocal();

延长它的生命周期,并在需要的时候主动调用remove清理,防止OOM。
为什么使用弱引用?如上我们将memory = null,表示要删除存储的数据,则此时ThreadLocal对象只剩弱可达性,那么GC启动后,Entry就会被清理;如果是强引用,那么ThreadLocal对象仍是可达的就不会被清理,容易引发OOM;在WeakHashMap中也是此设计;

虚引用

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
        return null;
    }

PhantomReference它的get方法返回null;
虚引用的使用场景很窄,在JDK中,目前只知道在申请堆外内存时有它的身影。
申请堆外内存时,在JVM堆中会创建一个对应的Cleaner对象,这个Cleaner类继承了PhantomReference,当DirectByteBuffer对象被回收时,可以执行对应的Cleaner对象的clean方法,做一些后续工作,这里是释放之前申请的堆外内存。
虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列中,用作记录它指向的对象已被回收。

Reference

除了强引用外,其他几种都继承自Reference;

    private T referent;         /* Treated specially by GC */

    volatile ReferenceQueue<? super T> queue;

    Reference next;

    transient private Reference<T> discovered;

    private static Reference<Object> pending = null;
  1. referent:指所要引用的对象
  2. queue:ReferenceQueue队列,存放Reference
  3. next: Reference进队列呈链表结构
  4. discovered:被JVM使用,表示下一个要被处理的Reference对象
  5. pending:被JVM使用,当前被处理的Reference对象;

Reference里有个ReferenceHandler线程,优先级极高,负责轮询检查pending值,不为空则放入队列;我们可以利用这点对要清初的对象进行些操作,如下例:

    public static void main(String[] args) {
        final ReferenceQueue queue = new ReferenceQueue();
        new Thread(){
            @Override
            public void run() {
                try {
                    Reference reference = queue.remove();
                    System.out.println(reference + "被回收");
                } catch (InterruptedException e) {

                }
            }
        }.start();
        Object ob = new Object();
        Reference root = new WeakReference(ob, queue);
        System.out.println(root);
        ob = null;
        System.gc();
    }

gc()启动后,弱引用对象ob要被清除于是被赋予到penging上,ReferenceHandler线程会将它放入到我们的queue当中,我们从queue取出进行打印操作;

参考

Java软引用究竟什么时候被回收
如何有效的避免OOM,温故Java中的引用
十分钟理解Java中的弱引用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值