欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-11 19:31:22
...

强引用

除非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://*.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的区别

相关标签: Reference