java中的Reference

这两天又重新学习了一下Reference,根据网上的资源做了汇总。

Java中的引用主要有4种:

  强引用 StrongReference: Object obj = new Object(); obj就为一个强引用,obj=null后, 该对象可能会被JVM回收

  软引用 SoftReference: 在内存不够用的时候,才会回收软引用的对象。

Object obj = new Object();
SoftReference<Object> softref = new SoftReference<Object>(obj);
obj = null;

  弱引用 WeakReference: new出来的对象没有强引用连接时,下一次GC时,就会回收该对象。

Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<Object>(obj);
obj = null;

  虚引用 PhantomReference: 与要与ReferenceQueue配合使用,它的get()方法永远返回null

JDK中的 java.lang.ref包:

  java.lang.ref包下主要都是reference相关的类,主要包括:

    FinalReference: 代表强引用,使没法直接使用。

    Finalizer:FinalReference的子类,主要处理finalize相关的工作

    PhantomReference: 虚引用

    Reference: 引用基类,abstract的

    ReferenceQueue: 引用轨迹队列

    SoftReference:软引用

    WeakedReference: 弱引用

ReferenceQueue

  引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中。

  

static ReferenceQueue NULL = new Null();    //初始化为null
static ReferenceQueue ENQUEUED = new Null();

static private class Lock { };
private Lock lock = new Lock();    //初始化为null
private volatile Reference<? extends T> head = null;    //Queue的header设定为null
private long queueLength = 0;

  queue主要有几种操作: enqueue, poll(非阻塞), remove(阻塞)

  enqueue的操作是添加一个对象到队列中。

boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
//本方法只能在Reference类中调用
        synchronized (r) {
 //防止将同一个Reference两次入队列
            if (r.queue == ENQUEUED) return false;
            synchronized (lock) {
//该对象入队列后,将该对象的queue对象置为 ENQUEUED
                r.queue = ENQUEUED;
                r.next = (head == null) ? r : head;
                head = r;
                queueLength++;
                if (r instanceof FinalReference) {
                    sun.misc.VM.addFinalRefCount(1);
                }
                lock.notifyAll();
                return true;
            }
        }
    }

入队列的实际操作就是将Reference对象r放到header的第一位

  poll的方法为:public Reference<? extends T> poll(),最终会调用 private Reference<? extends T> reallyPoll();具体操作与添加相反,若队列没有什值,直接返回null。

  remove的方法:public Reference<? extends T> remove(),最终会调用public Reference<? extends T> remove(long timeout),传递超时时间.

//阻塞,直到返回结果
public Reference<? extends T> remove(long timeout)
        throws IllegalArgumentException, InterruptedException
    {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout value");
        }
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
 //死循环
            for (;;) {
                lock.wait(timeout);
                r = reallyPoll();
                if (r != null) return r;
//若传递的等待时间不为0,说明等该timeout后也没有reference入队列,返回null,
//若timeout为0,则循环,直接队列有数据
                if (timeout != 0) return null;
            }
        }
    }

SoftReference

  SoftReference继承了抽象类Reference,自己内容有两个属性:

    /**
     * Timestamp clock, updated by the garbage collector
     */
//GC 会更新这一个静态变量的值
    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.
     */
//每次调用get方法时会更新timestampe
// this.timestamp = clock;
    private long timestamp;

  主要的逻辑还是在Reference类中:

  Reference提供了两个构造方法:

  

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

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

  同时有一些私有的属性:

  

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

    ReferenceQueue<? super T> queue;

    Reference next;
    transient private Reference<T> discovered;  /* used by VM */

    static private class Lock { };
    private static Lock lock = new Lock();

  最有意思的是有段static的代码块

  

 /* List of References waiting to be enqueued.  The collector adds
     * References to this list, while the Reference-handler thread removes
     * them.  This list is protected by the above lock object.
     */
//static类型,全局只有一个,GC一个Reference时,会把该Reference放到pending中,由于Reference有一个next属性,该处可能会是一些被GC过的引用的队列
    private static Reference pending = null;

    /* High-priority thread to enqueue pending References
     */
    private static class ReferenceHandler extends Thread {

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            for (;;) {

                Reference r;
                synchronized (lock) {
                    if (pending != null) {
//若pending不为null,将每个reference取出,
                        r = pending;
                        Reference rn = r.next;
                        pending = (rn == r) ? null : rn;
                        r.next = r;
                    } else {
    //若pending为null,等侍
                        try {
                            lock.wait();
                        } catch (InterruptedException x) { }
                        continue;
                    }
                }

                // Fast path for cleaners
                if (r instanceof Cleaner) {
                    ((Cleaner)r).clean();
                    continue;
                }

                ReferenceQueue q = r.queue;
//找到该Reference对象中的Queue,将自己添加到ReferenceQueue中
//这样就实现了软引用对象被回收后,在ReferenceQueue中就可以获取到。
                if (q != ReferenceQueue.NULL) q.enqueue(r);
            }
        }
    }
//启动一个线程(Reference Handler)处理引用
    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();
    }

  其它的一些操作如:get, clear, isEnqueued, enqueue,都是简单的操作。

WeakReference

  WeakReference也继承了Reference对象。

WeakReference与SoftReference

  这两上类都继承了Reference对象,基本的操作都一样的。唯一的区别就是SoftReference内部的属性(private long timestamp; 在每次get的时候会更新该值),VM有可能要GC的时候使用该字段来判断。这就和两类引用的区别相关连了,WeakReference每次GC时就会直接回收该引用的对象,而SoftReference只有在内存不够用的时候才会回收对象,而回收哪一个对象,可能就需要这个字段来区分。

FinalReference

class FinalReference<T> extends Reference<T> {

    public FinalReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

  这个类的权限是package,我们没法new出一个对象来使用。并且也只是继承了Reference类,没有别的特殊的操作。但同一个包有一类Finalizer,Finalizer继承了FinalReference,它也是一个package权限的类,同时是final的,不能被继承了。

    //全局一个ReferenceQueue
    private static ReferenceQueue queue = new ReferenceQueue();
    //全局只有一个unfinalized,也可以组成对象链
    private static Finalizer unfinalized = null;
    private static final Object lock = new Object();

    private Finalizer
        next = null,
        prev = null;

    // 将自己添加到unfinalized队列中
    private void add() {
        synchronized (lock) {
            if (unfinalized != null) {
                this.next = unfinalized;
                unfinalized.prev = this;
            }
            unfinalized = this;
        }
    }
//私有的构造方法
    private Finalizer(Object finalizee) {
//任何Finalizer对象的GC后都会到queue中
        super(finalizee, queue);
        add();
    }

/* Invoked by VM */
static void register(Object finalizee) {
  new Finalizer(finalizee);
}

 

  register执行的时候会new出对象,只有f类才会被JVM调用register。

  f类:实现了finalize方法并且非空的类。类的加载过程就已经标记为是否f类。

  Finalizer类最后有一些static的代码块:

private static class FinalizerThread extends Thread {
        private volatile boolean running;
        FinalizerThread(ThreadGroup g) {
            super(g, "Finalizer");
        }
        public void run() {
            if (running)
                return;

            // Finalizer thread starts before System.initializeSystemClass
            // is called.  Wait until JavaLangAccess is available
            while (!VM.isBooted()) {
                // delay until VM completes initialization
                try {
                    VM.awaitBooted();
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
            final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
            running = true;
            for (;;) {
                try {
//从ReferenceQueue中取出对象,执行对象的runFinalizer方法
                    Finalizer f = (Finalizer)queue.remove();
                    f.runFinalizer(jla);
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
        }
    }

    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread finalizer = new FinalizerThread(tg);
    //线程优先级低
        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
        finalizer.setDaemon(true);
        finalizer.start();
    }

  runFinalize方法会通过JVM调用object的finalize方法

private boolean hasBeenFinalized() {
        return (next == this);
    }
private void runFinalizer(JavaLangAccess jla) {
        synchronized (this) {
//若next==this,则表明this对象已经从unfinalized对象链中移除,已经执行过一次runFinalizer了
            if (hasBeenFinalized()) return;
//将该对象从unfinalized对象链中移除
             remove();
        }
        try {
            Object finalizee = this.get();
            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
//通过JDK调用对象的finalize方法
                jla.invokeFinalize(finalizee);

                /* Clear stack slot containing this variable, to decrease
                   the chances of false retention with a conservative GC */
                finalizee = null;
            }
        } catch (Throwable x) { }
        super.clear();
    }

  1 当GC发生时,GC算法会判断f累对象是不是只被Finalizer类引用

  2若这个累仅仅被Finalizer对象引用,说明这个对象在不就的将来会被回收,现在可以执行他的inalize方法了。

  3 将这个队形放到Finalizer类的ReferenceQueue中,但这个f类对象其实并没有被回收,因为Finalizer这个类还对他们保持引用。

  4 GC完成之前,JVM会调用ReferenceQueue中lock对象的notify方法,

  5 Finalizer的守护线程可能会被唤醒,从Queue中取出对象(remove),执行该Finalizer对象的runFinalizer方法(1 将自己从unfinalized对象链中去除,2 执行引用对象的finalize方法)

  6 下次GC时回收这个对象。

  • f对象因为Finalizer的引用而变成了一个临时的强引用,即使没有其他的强引用,还是无法立即被回收;
  • f对象至少经历两次GC才能被回收,因为只有在FinalizerThread执行完了f对象的finalize方法的情况下才有可能被下次GC回收,而有可能期间已经经历过多次GC了,但是一直还没执行f对象的finalize方法;
  • CPU资源比较稀缺的情况下FinalizerThread线程有可能因为优先级比较低而延迟执行f对象的finalize方法;
  • 因为f对象的finalize方法迟迟没有执行,有可能会导致大部分f对象进入到old分代,此时容易引发old分代的GC,甚至Full GC,GC暂停时间明显变长;
  • f对象的finalize方法被调用后,这个对象其实还并没有被回收,虽然可能在不久的将来会被回收。
  • You can see preference processing times (and number of references) in GC logs with -XX:+PrintReferenceGC

参考:

深入探讨 java.lang.ref 包

JVM源码分析之FinalReference完全解读

    

时间: 2024-11-12 07:41:40

java中的Reference的相关文章

java中的Reference抽象类

一.概述 位于java.lang.ref包下,声明:public abstract class Reference<T> extends Object 引用对象的抽象基类.此类定义了常用于所有引用对象的操作.因为引用对象是通过与垃圾回收器的密切合作来实现的,所以不能直接为此类创建子类. 二.方法详细 1.public T get()  返回此引用对象的指示对象.如果此引用对象已经由程序或垃圾回收器清除,则此方法将返回 null. 2.public void clear()  清除此引用对象.调

java中的4种reference的差别和使用场景(含理论、代码和执行结果)

我们知道java语言提供了4种引用类型:强引用.软引用(SoftReference).弱引用(WeakReference)和幽灵引用(PhantomReference),与引用密切相关的,还有一个引用队列ReferenceQueue.引用和引用队列的关系,对于垃圾回收来说非常重要,学习垃圾回收机制,必须要先了解引用和引用队列的使用方法.本文主要参考网上的一些理论,同时配合自己的一些测试代码,更好的理解这些概念.这篇博客也解决了 System.gc()和-XX:+DisableExplicitGC

Java中primitive type的线程安全性

Java中primite type,如char,integer,bool之类的,它们的读写操作都是atomic的,但是有几个例外: long和double类型不是atomic的,因为long和double都是8字节的,而在32位的CPU上,其机器字长为32位,操作8个字节需要多个指令操作. ++i或者i++,因为要先读后写,也是多步操作. 这些情况下,需要使用AutomicInteger,AutomicLong. 同时,java中的reference的读写也是automic的,虽然referen

理解Java中的弱引用(Weak Reference)

理解Java中的弱引用(Weak Reference) 本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出,谢谢大家:) 1. What——什么是弱引用? Java中的弱引用具体指的是java.lang.ref.WeakReference<T>类,我们首先来看一下官方文档对它做的说明: 弱引用对象的存在不会阻止它所指向的对象变被垃圾回收器回收.弱引

java中四种引用类型(对象的强、软、弱和虚引用)

对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期.这4种级别由高到低依次为:强引用.软引用.弱引用和虚引用. ⑴强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,Java虚拟机宁愿抛出O

java中的null注意事件总结

对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常(NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我认为Java发明者知道null与它解决的问题相比带来了更多的麻烦,但是null仍然陪伴着Java. 我越发感到惊奇,因为java的设计原理是为了简化事情,那就是为什么没有浪费时间在指针.操作符重载.多继承实现的原因,null却与此正好相反.好吧,我真的不知道这个问题的答案,我知道的是不管null被Ja

Java中的软(弱)引用

一.Java中的强.软.弱.虚引用 在JDK中我们可以看到有一个java.lang.ref的包,这个包中就是Java中实现强.软.弱.虚引用的包,如下: PhantomReference 虚引用:如果一个对象持有虚引用,就和没有持有引用一样,在任何时候都可能被垃圾回收器回收.虚引用主要用来跟踪对象被垃圾回收的活动,虚引用还有一个和弱.软引用不同的地方是虚引用必须和引用队列联合使用.当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象内存之前,把这个虚引用加入到与之关联的引用队列中

Java中关于WeakReference和WeakHashMap的理解

新美大的10月11日的笔试中有一道选择题,让选择函数返回结果,代码如下: 1 private static String test(){ 2 String a = new String("a"); 3 WeakReference<String> b = new WeakReference<String>(a); 4 WeakHashMap<String, Integer> weakMap = new WeakHashMap<String, In

转!!Java中关于Null的9个解释(Java Null详解)

对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常(NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我认为Java发明者知道null与它解决的问题相比带来了更多的麻烦,但是null仍然陪伴着Java. 我越发感到惊奇,因为java的设计原理是为了简化事情,那就是为什么没有浪费时间在指针.操作符重载.多继承实现的原因,null却与此正好相反.好吧,我真的不知道这个问题的答案,我知道的是不管null被Ja