美文网首页
你知道Java四种引用吗?以及他们是如何回收的?

你知道Java四种引用吗?以及他们是如何回收的?

作者: 小驴小驴 | 来源:发表于2020-12-24 15:22 被阅读0次

    Java四种引用

    • 强引用:有GC ROOT直接引用的对象,当没有再被GCROOT引用的时候,可以被垃圾回收
    • 软引用:当被引用的对象只被软引用时,当发生垃圾回收且内存空间匮乏时会删除软引用所引用的对象,可以通过引用队列来释放引用自身
    • 弱引用:当被引用的对象只被弱引用时,当发生垃圾回收时就会被回收弱引用所引用的对象,可以通过引用队列来释放引用自身
    • 虚引用:一般在ByteBuffer开辟直接内存联系,当ByteBuffer本身被垃圾回收之后,但是对于其开辟的内存空间无法被回收(这是因为直接内存不属于JVM所管理的内存,而属于操作系统内存)。因此虚引用会记录该直接内存地址,并且在虚引用的内部会有一个Cleaner对象,当ByteBuffer被垃圾回收之后,虚引用一定会进入引用队列,而该队列会有一个ReferenceHandler线程去调用队列中对象的clean方法去释放直接内存。

    引用队列

    引用队列 】用于将软引用和弱引用、虚引用的引用者放入该队列,当他们所引用的对象被垃圾回收之后,Reference引用关系就会进入该队列等待被线程回收。其中软引用和弱引用可以不配合引用队列使用,但是虚引用一定需要配合该队列。

    实例程序

    前提:为了测出效果,这里把堆内存的大小修改为20M,在启动参数中添加-Xmx20m;

    - 强引用
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();    
        for (int i = 0; i < 4; i++) {
            list.add(new byte[1024 * 1024 * 5]);    
        }
    }
    运行结果:
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    

    说明:
    这种是强引用的案例,我们日常工作中大多数对象都是被强引用。即GC Roots所能直接或间接的引用到的对象。这部分在GC时不会被回收,因为这些byte[]始终被生命周期更长的list引用着。因此当byte[]太多之后势必造成堆OOM。

    - 软引用
    public static void soft() {
        List<SoftReference<byte[]>> lists = new ArrayList<>();    
        for (int i = 0; i < 5; i++) {
            lists.add(new SoftReference<>(new byte[1024 * 1024 * 4]));
            System.out.println("==============================");
            for (SoftReference<byte[]> list : lists) {
                 System.out.println(list.get()); 
            }
        }
    }
    运行结果:
    ==============================
    [B@682a0b20
    ==============================
    [B@682a0b20
    [B@3d075dc0
    ==============================
    [B@682a0b20
    [B@3d075dc0
    [B@214c265e
    ==============================
    [B@682a0b20
    [B@3d075dc0
    [B@214c265e
    [B@448139f0
    ==============================
    null
    null
    null
    null
    [B@7cca494b
    

    说明:软引用在发生GC时,且堆内存空间不足时,就会释放被软引用所引用的对象。

    - 弱引用
    public static void weak() {
        List<WeakReference<byte[]>> lists = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            lists.add(new WeakReference<>(new byte[1024*1024*4]));
            System.out.println("========================");
            for (WeakReference<byte[]> list : lists) {
                System.out.println(list.get());
            }
       }
    }
    运行结果:
    ========================
    [B@682a0b20
    ========================
    [B@682a0b20
    [B@3d075dc0
    ========================
    [B@682a0b20
    [B@3d075dc0
    [B@214c265e
    ========================
    null
    null
    null
    [B@448139f0
    ========================
    null
    null
    null
    [B@448139f0
    [B@7cca494b
    

    说明:软引用在GC时,不管内存是否够用都会删除只被软引用所引用的对象。

    - 虚引用
    public static void fake() throws IOException {
        Runnable runnable = () -> System.out.println("虚引用");
        Object obj = new Object();
        Cleaner.create(obj, runnable);
        System.in.read();
        obj = null;
        System.gc();
        System.in.read();
    }
    运行结果:
    虚引用
    

    说明:如上是一个虚引用的小Demo,也是ByteBuffer开辟直接内存的一个回收缩影。上面的核心类为Cleaner类,该类继承PhantomReference类,所以其本质就是一个虚引用。上述的create方法就是使用虚引用来引用obj对象,当obj对象被垃圾释放时,引用关系会被放入到ReferenceQueue队列中,之后会有一个线程ReferenceHandler线程一直从Queue中取出Reference对象并调用clean方法,而clean方法就会调用create传入的第二个线程参数。
    源码如下:

    • 调用Cleaner.create方法时,会构造Cleaner对象,并将传入的Runnable赋值给全局变量thunk:


      1485_0.png
    • clean方法被调用时,会调用thunk线程的run执行用户自定义的释放动作:


      1487_0.png
    • Reference中有一个线程以最低优先级一直在运行:


      1489_0.png
    • 如下是上述图片中的tryHandlePending方法,方法比较长,这里只截取一段,其中c是Cleaner的实例:


      1491_0.png
    • 这里你可能有个疑问,就是那么多的Cleaner对象,他怎么知道调用哪个Cleaner对象的clean方法呢?


      1493_0.png

      看来这一切都是这个pending的变量才是源头,看一下这个变量的定义吧,注释说的还是很清晰的:


      1495_0.png

    回顾引用队列

    在了解了上述背景之后,你可能想问。在软、弱引用。在被引用的对象在相应的场景被释放之后。那么这一层Reference引用关系是如何被清理的呢?仔细看一下下一段源码:


    1497_0.png

    可以看到,ReferenceHandler线程会把垃圾回收器带来的Reference对象置空以下一次被垃圾回收(这个discovered就是Reference实例)。

    相关文章

      网友评论

          本文标题:你知道Java四种引用吗?以及他们是如何回收的?

          本文链接:https://www.haomeiwen.com/subject/mtzpnktx.html