強引用、軟引用、弱引用、虛引用測試

強引用

除非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的區別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章