zoukankan      html  css  js  c++  java
  • 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完全解读 

        

  • 相关阅读:
    javascript的alert的使用
    UIGestureRecognizer对图像进行缩放、移动、旋转操作
    对开源库使用 AutoCAD 文件格式
    计算机图形学常用算法
    KMP字符串模式匹配详解
    C++面试题String函数实现
    c++虚函数解析
    c++内存分配
    win32编程入门
    C++中Int转换成String
  • 原文地址:https://www.cnblogs.com/zyzl/p/5540248.html
Copyright © 2011-2022 走看看