zoukankan      html  css  js  c++  java
  • 强引用、软引用、弱引用、虚引用测试

    强引用

    除非GC Roots不可达,否则宁愿OOM也不回收引用

    /**
     * 除非GC Roots不可达,否则宁愿OOM也不回收引用
     * 启动参数:
     * -Xmx16m  -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
     */
    private static void strongRefenceTest() {
        final int _8M = 8 * 1024 * 1024;
        List<byte []> list = new ArrayList<byte[]>();
        System.out.println("add 8m  -1");
        list.add(new byte[_8M]);
        System.out.println("add 8m  -2");
        list.add(new byte[_8M]);//OOM异常
        list.stream().forEach(r-> System.out.println(r));
    }
    

    效果
    在这里插入图片描述

    软引用

    内存足够,不会回收软引用对应的内存。内存不足,GC时会回收其内存

    1.手动gc

     /**
         * 软引用手动gc ,由于内存足够,不会回收软引用对应的内存
         * @throws InterruptedException
         */
        private static void softReferenceGCByManual() throws InterruptedException {
            final int _1M = 1 * 1024 * 1024;//直接改为1M测试
            List<SoftReference> list = new ArrayList<SoftReference>();
            System.out.println("add 1m  -1");
            list.add(new SoftReference(new byte[_1M]));
            System.out.println("add 1m  -2");
            list.add(new SoftReference(new byte[_1M]));
            System.out.println("add 1m  -3");
            list.add(new SoftReference(new byte[_1M]));
            System.gc();//内存足够,不会回收软引用对应的内存
            System.out.println("gc---");
            TimeUnit.SECONDS.sleep(1);
            list.stream().forEach(r-> System.out.println(r.get()));
        }
    

    效果:内存足够不会导致回收软引用对应的内存
    在这里插入图片描述

    2.内存不够导致gc

     /**
         *  仅仅gc, 还不会回收。需内存不够 GC的时候才回收软引用。
         * 启动参数:
         * -Xmx16m  -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
         */
        private static void softRefenceTest() {
            final int _8M = 8 * 1024 * 1024;
            List<SoftReference> list = new ArrayList<SoftReference>();
            System.out.println("add 8m  -1");
            list.add(new SoftReference(new byte[_8M]));
            System.out.println("add 8m  -2");
            list.add(new SoftReference(new byte[_8M]));
            System.out.println("add 8m  -3");
            list.add(new SoftReference(new byte[_8M]));
            list.stream().forEach(r-> System.out.println(r.get()));
        }
    

    效果:内存不够导致,list中第0,第1个元素被回收。
    在这里插入图片描述

    弱引用

    只要gc就会回收内存

     /**
         * 只要gc就会回收
         * 启动参数:
         * -Xmx16m  -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
         */
        private static void WeakReferenceTest() {
            Integer a = new Integer(8888);
            WeakReference bRef = new WeakReference(a);
            a = null;//若不去掉强引用,a对应的Integer对象不能被回收
            System.out.println("Run gc");
            System.gc();
            System.out.println("a:" + bRef.get());
        }
    

    效果:只要gc就会回收
    在这里插入图片描述

    虚引用

    主要用于代替finalize()做一些清理操作。
    1.PhantomReference#get永远返回null;
    2.被回收的时候,会被投入相关的ReferenceQueue,可以轮询ReferenceQueue来处理被gc回收的资源。

    展示特性

    /**
         * -Xmx15m  -XX:NewRatio=4 -XX:+PrintGCDetails -XX:+PrintGCDateStamps
         * @throws InterruptedException
         */
        private static void testPhantomReference() throws InterruptedException {
            final int _6M = 6 * 1024 * 1024;//可直接改为1M测试
            ReferenceQueue referenceQueue = new ReferenceQueue();
            List<Reference> list = new ArrayList<Reference>();
            System.out.println("add 6m  -1");
            PhantomReference aRef = new PhantomReference(new byte[_6M], referenceQueue);//A
            System.out.println(aRef.get());//null。避免暴露value造成强引用
            list.add(aRef);
            System.out.println("add 6m  -2");
            list.add(new PhantomReference(new byte[_6M], referenceQueue));
            System.gc();// 不gc 不会入ReferenceQueue  。若不手动gc,调低数组大小后 ,可能不引起gc导致不入队
            list.stream().forEach(r-> System.out.println("refence isEnqueued:"+r.isEnqueued()));//若不gc 则不入队
            //入队了两次 。 必须移除队列中对PhantomReference的引用.否则不会回收
            if (referenceQueue.remove() != null) {
                System.out.println("clean before gc");
            }
            if (referenceQueue.remove() != null) {
                System.out.println("clean before gc");
            }
            aRef=null;//若不清除该强引用, aRef 所对应的value不会被回收
            list.clear();//若不清除对reference的强引用。  list中所有 reference所包含的value不会被回收
            //System.gc();//此处可以不手动gc , 已经释放了对PhantomReference的引用-referenceQueue、list、aRef ,再次申请空间的时候会回收不可达的PhantomReference
            // 内存不够,引起gc.回收reference所包含的value
            System.out.println("add 6m  -3");
            list.add(new PhantomReference(new byte[_6M], referenceQueue));
            System.out.println("add 8m  -4");
            list.add(new PhantomReference(new byte[_6M], referenceQueue));
        }
    

    演示代替finalize()回收时自动清理

    /**
     * 演示自动关闭
     * https://stackoverflow.com/questions/43311825/how-to-use-phantomreference-as-finalize-replacement
     * @Author Holger
     * @Date 2019/10/24 0024 下午 4:44
     */
    public class TestPhantomReference {
        public static void main(String[] args) throws InterruptedException {
            // create two Test Objects without closing them
            for (int i = 0; i < 2; i++) {
                new Test(i);
            }
            // create two Test Objects with proper resource management
            try(Test t2=new Test(2); Test t3=new Test(3)) {
                System.out.println("using Test 2 and 3");
            }
    
            // Sleep 1 Second, run GC, sleep 1 Second
            Thread.sleep(1000);
            System.out.println("System.gc()");
            System.gc();
            Thread.sleep(1000);
        }
    
        static class TestResource extends PhantomReference<Test> {
            private int id;
            private TestResource(int id, Test referent, ReferenceQueue<Test> queue) {
                super(referent, queue);
                this.id = id;
            }
            private void close() {
                System.out.println("closed "+id);
            }
        }
        public static class Test implements AutoCloseable {
            static AutoCloseThread thread = new AutoCloseThread();
            static { thread.start(); }
            private final TestResource resource;
            Test(int id) {
                resource = thread.addObject(this, id);
            }
            public void close() {
                resource.close();
                thread.remove(resource);
            }
        }
    
        public static class AutoCloseThread extends Thread {
            private ReferenceQueue<Test> mReferenceQueue = new ReferenceQueue<>();
            private Set<TestResource> mPhantomStack = new HashSet<>();
    
            public AutoCloseThread() {
                setDaemon(true);
            }
            TestResource addObject(Test pTest, int id) {
                final TestResource rs = new TestResource(id, pTest, mReferenceQueue);
                mPhantomStack.add(rs);
                return rs;
            }
            void remove(TestResource rs) {
                mPhantomStack.remove(rs);
            }
    
            @Override
            public void run() {
                try {
                    while (true) {
                        TestResource rs = (TestResource)mReferenceQueue.remove();
                        System.out.println(rs.id+" not properly closed, doing it now");
                        mPhantomStack.remove(rs);
                        rs.close();
                    }
                } catch (InterruptedException e) {
                    System.out.println("Thread Interrupted");
                }
            }
        }
    }
    

    效果:没有使用try-resource的,在被回收的时候被自动关闭了。
    在这里插入图片描述

    演示代替finalize()回收时自动清理2

    public class TestPhantomReference2 {
        static class Person extends PhantomReference{
            int id;
            private Person(Object referent, ReferenceQueue q) {
                super(referent, q);
            }
            public Person(ReferenceQueue q, int id, Object personData) {
                super(personData, q);
                this.id = id;
            }
        }
        public static void main(String[] args) throws InterruptedException {
            final int _6M = 6 * 1024 * 1024;
            ReferenceQueue<Object> queue = new ReferenceQueue<>();
            PhantomReference<Object> pf = new Person(queue, 0, new byte[_6M]);
            new Thread(() -> {
                Reference removed;
                try {
                    while ((removed = queue.remove()) != null) {
                        System.out.println("clean:"+((Person)removed).id);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            int i=1;
            while (true) {
                Thread.sleep(2000);
                pf=new Person(queue, i++, new byte[_6M]);
                /*System.gc();
                if (pf.get() != null) {
                    System.out.println("phantomReference Queued: " + pf.isEnqueued()+" => phantomReference : exists");
                } else {
                    System.out.println("phantomReference Queued: " + pf.isEnqueued()+" => phantomReference : null");
                }*/
            }
        }
    }
    

    在这里插入图片描述

    Have you ever used PhantomReference in any project?
    Java Reference Objects
    java:四种Reference的区别

  • 相关阅读:
    Docker 基础 B站 学习 最强 教程
    apache+php安装
    php拓展 swoole 安装
    go beego框架 入门使用 (一)
    php 使用 phpword 操作 word 读取 word
    linux + MongoDB 安装 + 部署 + 讲解 (满满干货看完记得收藏噢)
    Thanos设计简析
    Prometheus TSDB文件格式-index
    Linux Kernel文件系统写I/O流程代码分析(二)bdi_writeback
    Linux Kernel文件系统写I/O流程代码分析(一)
  • 原文地址:https://www.cnblogs.com/thewindkee/p/12873115.html
Copyright © 2011-2022 走看看