强引用、软引用、弱引用、虚引用测试

强引用

除非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的区别

posted @ 2019-10-24 15:36  thewindkee  阅读(222)  评论(0编辑  收藏  举报