62.Java中几种不同的引用-强软弱虚

 

1.Java中几种不同的引用-强软弱虚概述

  1. java中的引用分为4种,强引用、软引用、弱引用、虚引用,引用强度依次逐渐减弱,都继承自Reference
    在这里插入图片描述
  2. 四种引用概述
    强引用:最传统的引用的定义,在开发中99%都是使用的强引用。即类似“Object obj = new Object()”这种引用关系。无论任何情况下,只要强引用的关系存在,垃圾收集器就永远不会回收掉被引用的对象。
    软引用:在系统将要发生内存溢出之前,将会把这些被软引用的对象列入回收范围之中进行回收。如果这次回收之后还没有足够的内存,才会抛出内存溢出异常。概括为:内存不足即回收。
    弱引用:当垃圾收集的时候,无论内存空间是否足够,被弱引用关联的对象就会被回收掉。概括为:发现即回收。
    虚引用:虚引用不会影响对象生存的时间。使用虚引用的目的是为了能在对象被垃圾回收器回收时收到一个系统通知。概括为:对象回收跟踪。
    在这里插入图片描述

1.1 强引用

在这里插入图片描述
强引用特点:
在这里插入图片描述

1.2 软引用

当内存足够时不会回收软引用对象,当内存不足时,才会回收软引用对象。
在这里插入图片描述
软引用使用:使用java.lang.ref.SoftReference类来实现软引用。
在这里插入图片描述
例子:

public class SoftReferenceTest {
    public static class User {
        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int id;
        public String name;

        @Override
        public String toString() {
            return "[id=" + id + ", name=" + name + "] ";
        }
    }

    public static void main(String[] args) {
        //创建对象,建立软引用
//        SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "songhk"));
        //上面的一行代码,等价于如下的三行代码
        User u1 = new User(1,"songhk");
        SoftReference<User> userSoftRef = new SoftReference<User>(u1);
        u1 = null;//取消强引用


        //从软引用中重新获得强引用对象
        System.out.println(userSoftRef.get()); // 拿到创建的User对象

        System.gc();
        System.out.println("After GC:");
//        //垃圾回收之后获得软引用中的对象
        System.out.println(userSoftRef.get());//如果堆空间内存足够,则不会回收软引用的可达对象。否则,会回收这个对象,输出null。
//
        try {
            //让系统认为内存资源紧张、不够
//            byte[] b = new byte[1024 * 1024 * 7];
            byte[] b = new byte[1024 * 7168 - 635 * 1024];
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            //再次从软引用中获取数据
            System.out.println(userSoftRef.get());//在报OOM之前,垃圾回收器会回收软引用的可达对象。
        }
    }
}

1.3 弱引用

只被弱引用关联的对象在垃圾回收的时候就会被回收。
软引用、弱引用非常适合来保存那些可有可无的缓存数据。当内存足够的时候,这些缓存数据可以存着很长的时间;当内存不足的时候,这些缓存数据就可以被回收,不会导致内存溢出。
在这里插入图片描述
使用方法:
在这里插入图片描述
软引用与弱引用的不同:当进行GC的时候,需要通过算法来检查是否回收软引用对象(判断内存是否足够),而对于弱引用对象,GC总是进行回收。

例子:

public class WeakReferenceTest {
    public static class User {
        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int id;
        public String name;

        @Override
        public String toString() {
            return "[id=" + id + ", name=" + name + "] ";
        }
    }

    public static void main(String[] args) {
        //构造了弱引用
        WeakReference<User> userWeakRef = new WeakReference<User>(new User(1, "songhk"));
        //从弱引用中重新获取对象
        System.out.println(userWeakRef.get());

        System.gc();
        // 不管当前内存空间足够与否,都会回收它的内存
        System.out.println("After GC:");
        //重新尝试从弱引用中获取对象
        System.out.println(userWeakRef.get());
    }
}

1.4 虚引用

一个对象是否存在虚引用,完全不会影响对象的生命周期。为一个对象设置虚引用关联的唯一目的就是跟踪垃圾回收过程。比如,在这个对象被回收的时候,收到一个系统通知。
在这里插入图片描述
虚引用必须与引用队列一起使用。当垃圾回收器回收一个对象时,如果发现它还有虚引用,就会在回收对象之后,将虚引用加入到引用队列中,以通知应用程序对象的回收情况。
在这里插入图片描述
例子:

public class PhantomReferenceTest {
    public static PhantomReferenceTest obj;//当前类对象的声明
    static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;//引用队列

    public static class CheckRefQueue extends Thread {
        @Override
        public void run() {
            while (true) {
                if (phantomQueue != null) {
                    PhantomReference<PhantomReferenceTest> objt = null;
                    try {
                        objt = (PhantomReference<PhantomReferenceTest>) phantomQueue.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (objt != null) {
                        System.out.println("追踪垃圾回收过程:PhantomReferenceTest实例被GC了");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable { //finalize()方法只能被调用一次!
        super.finalize();
        System.out.println("调用当前类的finalize()方法");
        obj = this;
    }

    public static void main(String[] args) {
        Thread t = new CheckRefQueue();
        t.setDaemon(true);//设置为守护线程:当程序中没有非守护线程时,守护线程也就执行结束。
        t.start();

        phantomQueue = new ReferenceQueue<PhantomReferenceTest>();
        obj = new PhantomReferenceTest();
        //构造了 PhantomReferenceTest 对象的虚引用,并指定了引用队列
        PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<PhantomReferenceTest>(obj, phantomQueue);

        try {
            //不可获取虚引用中的对象
            System.out.println(phantomRef.get());

            //将强引用去除
            obj = null;
            //第一次进行GC,由于对象可复活,GC无法回收该对象
            System.gc();
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("obj 是 null");
            } else {
                System.out.println("obj 可用");
            }
            System.out.println("第 2 次 gc");
            obj = null;
            System.gc(); //一旦将obj对象回收,就会将此虚引用存放到引用队列中。
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("obj 是 null");
            } else {
                System.out.println("obj 可用");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

posted @ 2020-11-18 23:49  跃小云  阅读(91)  评论(0编辑  收藏  举报