• Java8-Reference


    Reference JDK有四个子类,分为WeakReference,SoftReference,PhantomReference,FinalReference。

    WeakReference

    弱引用,JVM扫描到就开始回收里面的T,注意,这里的回收是指的是JVM的操作,对于已经入过队WeakReference JVM不会处理。

    ReferenceQueue<String> queue = new ReferenceQueue<>();
            LinkedList<WeakReference<String>> weakList = new LinkedList<WeakReference<String>>();
            for (int i = 0; i < 5; i++)
            {
                WeakReference<String> s = new WeakReference<String>(i + "", queue);
                weakList.add(s);
    //            s.enqueue();
                System.out.println(s.isEnqueued()+" "+s.get());
    //            s.clear();
            }
            System.gc();
            try
            { // 下面休息几分钟,让上面的垃圾回收线程运行完成
                Thread.sleep(6000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            Reference<? extends String> s = null;
            while ((s = queue.poll()) != null)
            {
                System.out.println(s.get()+" "+s.isEnqueued());
            }

    输出:

    true 0
    true 1
    true 2
    true 3
    true 4
    null false
    null false
    null false
    null false
    null false

    下面这段,自己手工入队

    ReferenceQueue<String> queue = new ReferenceQueue<>();
            LinkedList<WeakReference<String>> weakList = new LinkedList<WeakReference<String>>();
            for (int i = 0; i < 5; i++)
            {
                WeakReference<String> s = new WeakReference<String>(i + "", queue);
                weakList.add(s);
                s.enqueue();
                System.out.println(s.isEnqueued()+" "+s.get());
    //            s.clear();
            }
            System.gc();
            try
            { // 下面休息几分钟,让上面的垃圾回收线程运行完成
                Thread.sleep(6000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            Reference<? extends String> s = null;
            while ((s = queue.poll()) != null)
            {
                System.out.println(s.get()+" "+s.isEnqueued());
            }
    true 0
    true 1
    true 2
    true 3
    true 4
    4 false
    3 false
    2 false
    1 false
    0 false

    T并没有回收,还在哪里,因为手动入队之后,没有执行clear.

    SoftReference

    软引用,JVM没有内存的时候就开始回收,注意,这里的回收是指的是JVM的操作,与上面WeakReference类似,只是JVM清理的时机不同。

        ReferenceQueue<String> queue = new ReferenceQueue<>();
            LinkedList<SoftReference<String>> weakList = new LinkedList<SoftReference<String>>();
            for (int i = 0; i < 5; i++)
            {
                SoftReference<String> s = new SoftReference<String>(i + "", queue);
                weakList.add(s);
                s.enqueue();
                System.out.println(s.isEnqueued()+" "+s.get());
                s.clear();
            }
            System.gc();
            try
            { // 下面休息几分钟,让上面的垃圾回收线程运行完成
                Thread.sleep(6000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            Reference<? extends String> s = null;
            while ((s = queue.poll()) != null)
            {
                System.out.println(s.get()+" "+s.isEnqueued());
            }

    手动入队的。

    true 0
    true 1
    true 2
    true 3
    true 4
    null false
    null false
    null false
    null false
    null false

    PhantomReference

    幽灵引用,get()永远返回null,但是构造函数的T会被保存,并且在JVM对幽灵引用入队之后,T还存在.

    ReferenceQueue<String> queue = new ReferenceQueue<>();
            LinkedList<PhantomReference<String>> weakList = new LinkedList<PhantomReference<String>>();
            for (int i = 0; i < 5; i++)
            {
                PhantomReference<String> s = new PhantomReference<String>(i + "", queue);
                weakList.add(s);
                System.out.println(s.isEnqueued()+" "+s.get());
            }
            System.gc();
            try
            { // 下面休息几分钟,让上面的垃圾回收线程运行完成
                Thread.sleep(6000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            Reference<? extends String> s = null;
            while ((s = queue.poll()) != null)
            {
                System.out.println(s.get()+" "+s.isEnqueued());
            }
    false null
    false null
    false null
    false null
    false null
    null false
    null false
    null false
    null false
    null false

    注意,reference没有入队,这个在Java8里面是,JVM不保证会立即入队,原文是(at that time or at some later time it will enqueue),Java9变成了立即入队。T通过get方法获取不到.

    FinalReference

    FinalReference的有个子类是Finalizer,如果某个类实现类Object类的finalize()方法,并不为空,那么在对象生成的时候,会被包装成Finalizer,这个操作由JVM来实现,对程序员透明。

        private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
        private static Finalizer unfinalized = null;
        private static final Object lock = new Object();
    
        private Finalizer
            next = null,
            prev = null;

    注意上面Finalizer的源码,存在一个queue和unfinalized.

    queue的作用和上面三个Reference一样,用于实现通知,表示对象要被回收了(JVM)操作。

    unfinalized 用于保持强引用,防止当前的Finalizer 对象被回收。

    在代码加载的时候,会初始化一个 FinalizerThread 线程来执行 runFinalizer 方法

        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 {
                        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();
        }

    注意,这个线程是一个守护者线程,在后台默默的执行finalize()。不会阻止JVM关闭。

    注意这个方法。

     private void runFinalizer(JavaLangAccess jla) {
            synchronized (this) {
                if (hasBeenFinalized()) return;
                remove();
            }
            try {
                Object finalizee = this.get();
                if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
                    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();
        }

    如果已经执行过finalize(),移除Finalizer对象,要不然就移除当前的 Finalizer,要在第二次GC的时候才会回收Finalizer。

    当虚拟机要关闭的时候的finalize()调用过程。

        /* Invoked by java.lang.Shutdown */
        static void runAllFinalizers() {
            if (!VM.isBooted()) {
                return;
            }
    
            forkSecondaryFinalizer(new Runnable() {
                private volatile boolean running;
                public void run() {
                    if (running)
                        return;
                    final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
                    running = true;
                    for (;;) {
                        Finalizer f;
                        synchronized (lock) {
                            f = unfinalized;
                            if (f == null) break;
                            unfinalized = f.next;
                        }
                        f.runFinalizer(jla);
                    }}});
        }

    fork-join的搞法。开启一个线程去执行finalize()

    private static void forkSecondaryFinalizer(final Runnable proc) {
            AccessController.doPrivileged(
                new PrivilegedAction<Void>() {
                    public Void run() {
                    ThreadGroup tg = Thread.currentThread().getThreadGroup();
                    for (ThreadGroup tgn = tg;
                         tgn != null;
                         tg = tgn, tgn = tg.getParent());
                    Thread sft = new Thread(tg, proc, "Secondary finalizer");
                    sft.start();
                    try {
                        sft.join();
                    } catch (InterruptedException x) {
                        /* Ignore */
                    }
                    return null;
                    }});
        }

    另外一种调用RunAllFinalizers()方法的方法,后面的逻辑和shutdown调用一致。这个方法不推荐被使用,可能会在存活的对象上调用,这样才是正确做

    Runtime.getRuntime().runFinalization();

        /* Called by Runtime.runFinalization() */
        static void runFinalization() {
            if (!VM.isBooted()) {
                return;
            }
    
            forkSecondaryFinalizer(new Runnable() {
                private volatile boolean running;
                public void run() {
                    if (running)
                        return;
                    final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
                    running = true;
                    for (;;) {
                        Finalizer f = (Finalizer)queue.poll();
                        if (f == null) break;
                        f.runFinalizer(jla);
                    }
                }
            });
        }

    测试代码

        public static void main(String[] args) throws InterruptedException
        {
            for(int i=0;i< 2;i++)
            {
                Test t = new Test();
            }
            Runtime.getRuntime().runFinalization();
    //        Thread.sleep(6000);
        }
        
        
        private static class Test
        {
            public void print() {System.out.println("t");}
            @Override
            protected void finalize() throws Throwable
            {
                System.out.println("1231");
            }
        }
  • 相关阅读:
    20150306+Linux安装+常用命令-01
    补充:javascript
    补充:数组循环与思路
    补充:控制语句
    DOM操作的概念
    什么是数组?
    补充:MySQL整理
    MySQL数据查询
    补充:MySQL经典45道题型
    表单 form:标签、类型、注意事项
  • 原文地址:https://www.cnblogs.com/shuiyonglewodezzzzz/p/11139014.html
Copyright © 2020-2023  润新知