强软弱虚引用,只有体会过了,才能记住

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 强软弱虚引用,只有体会过了,才能记住

强软弱虚引用,只有体会过了,才能记住
作者:CoderBear juejin.im/post/5e65b8096fb9a07cbb6e4a43

juejin.im/post/5e65b8096fb9a07cbb6e4a43

以前学习强软弱虚引用的时候,只是走马观花看看博客,并没有自己写代码去实践、去证明,导致每次看完后,过不了多久就忘了,后来下定决心,一定要自己敲敲代码,这样才能让印象更加深刻,古人云:纸上得来终觉浅,绝知此事要躬行。

Java中的四种引用

Java中有四种引用类型:强引用、软引用、弱引用、虚引用。

Java为什么要设计这四种引用

Java的内存分配和内存回收,都不需要程序员负责,都是由伟大的JVM去负责,一个对象是否可以被回收,主要看是否有引用指向此对象,说的专业点,叫可达性分析。

Java设计这四种引用的主要目的有两个:

  • 可以让程序员通过代码的方式来决定某个[对象的生命周期](http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247491186&idx=2&sn=78cf718d5ce5c591a9fddd28c5931c0d&chksm=ebd6215edca1a84824d48032eef62fd5276b14c484d1691ff3054a051b794a2e01a1663b60fa&scene=21#wechat_redirect);
  • 有利用垃圾回收。
  • 有利用垃圾回收。

    强引用

    强引用是最普遍的一种引用,我们写的代码,99.9999%都是强引用:

    
    Object o = new Object();
    

    这种就是强引用了,是不是在代码中随处可见,最亲切。

    只要某个对象有强引用与之关联,这个对象永远不会被回收,即使内存不足,JVM宁愿抛出OOM,也不会去回收。

    那么什么时候才可以被回收呢?当强引用和对象之间的关联被中断了,就可以被回收了。

    我们可以手动把关联给中断了,方法也特别简单:

    
    o = null;
    

    我们可以手动调用GC,看看如果强引用和对象之间的关联被中断了,资源会不会被回收,为了更方便、更清楚的观察到回收的情况,我们需要新写一个类,然后重写finalize方法,下面我们来进行这个实验:

    
    public class Student {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Student 被回收了");
        }
    }
    
    
    public static void main(String[] args) {
            Student student = new Student();
            student = null;
            System.gc();
    }
    

    运行结果:

    
    Student 被回收了
    

    可以很清楚的看到资源被回收了。

    当然,在实际开发中,千万不要重写finalize方法

    在实际的开发中,看到有一些对象被手动赋值为NULL,很大可能就是为了“特意提醒”JVM这块资源可以进行垃圾回收了。

    软引用

    下面先来看看如何创建一个软引用:

    
     SoftReferenceStudentstudentSoftReference=new SoftReferenceStudent(new Student());
    

    软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只要get一下就可以了:

    
    SoftReferenceStudentstudentSoftReference=new SoftReferenceStudent(new Student());
    Student student = studentSoftReference.get();
    System.out.println(student);
    

    软引用有什么特点呢:当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。

    还是一样的,必须做实验,才能加深印象:

    
    SoftReferencebyte[] softReference = new SoftReferencebyte[](new byte[1024*1024*10]);
    System.out.println(softReference.get());
    System.gc();
    System.out.println(softReference.get());
    
    byte[] bytes = new byte[1024 * 1024 * 10];
    System.out.println(softReference.get());
    

    我定义了一个软引用对象,里面包裹了byte[],byte[]占用了10M,然后又创建了10Mbyte[]。

    运行程序,需要带上一个参数:

    
     -Xmx20M
    

    代表最大堆内存是20M。

    运行结果:

    
    [B@11d7fff
    [B@11d7fff
    null
    

    可以很清楚的看到手动完成GC后,软引用对象包裹的byte[]还活的好好的,但是当我们创建了一个10M的byte[]后,最大堆内存不够了,所以把软引用对象包裹的byte[]给干掉了,如果不干掉,就会抛出OOM

    软引用到底有什么用呢?比较适合用作缓存,当内存足够,可以正常的拿到缓存,当内存不够,就会先干掉缓存,不至于马上抛出OOM。

    弱引用

    弱引用的使用和软引用类似,只是关键字变成了WeakReference:

    
            WeakReferencebyte[] weakReference = new WeakReferencebyte[](new byte[1024*1024*10]);
            System.out.println(weakReference.get());
    

    弱引用的特点是不管内存是否足够,只要发生GC,都会被回收:

    
            WeakReferencebyte[] weakReference = new WeakReferencebyte[](new byte[1]);
            System.out.println(weakReference.get());
            System.gc();
            System.out.println(weakReference.get());
    

    运行结果:

    
    [B@11d7fff
    null
    

    可以很清楚的看到明明内存还很充足,但是触发了GC,资源还是被回收了。弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。

    虚引用

    虚引用又被称为幻影引用,我们来看看它的使用:

    
            ReferenceQueue queue = new ReferenceQueue();
            PhantomReferencebyte[] reference = new PhantomReferencebyte[](new byte[1], queue);
            System.out.println(reference.get());
    

    虚引用的使用和上面说的软引用、弱引用的区别还是挺大的,我们先不管ReferenceQueue 是个什么鬼,直接来运行:

    
    null
    

    竟然打印出了null,我们来看看get方法的源码:

    
        public T get() {
            return null;
        }
    

    这是几个意思,竟然直接返回了null。

    这就是虚引用特点之一了:无法通过虚引用来获取对一个对象的真实引用。

    那虚引用存在的意义是什么呢?这就要回到我们上面的代码了,我们把代码复制下,以免大家再次往上翻:

    
            ReferenceQueue queue = new ReferenceQueue();
            PhantomReferencebyte[] reference = new PhantomReferencebyte[](new byte[1], queue);
            System.out.println(reference.get());
    

    创建虚引用对象,我们除了把包裹的对象传了进去,还传了一个ReferenceQueue,从名字就可以看出它是一个队列。

    虚引用的特点之二就是 虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。

    我们来用代码实践下吧:

    
            ReferenceQueue queue = new ReferenceQueue();
            Listbyte[] bytes = new ArrayList();
            PhantomReferenceStudent reference = new PhantomReferenceStudent(new Student(),queue);
            new Thread(() - {
                for (int i = 0; i  100;i++ ) {
                    bytes.add(new byte[1024 * 1024]);
                }
            }).start();
    
            new Thread(() - {
                while (true) {
                    Reference poll = queue.poll();
                    if (poll != null) {
                        System.out.println("虚引用被回收了:" + poll);
                    }
                }
            }).start();
            Scanner scanner = new Scanner(System.in);
            scanner.hasNext();
        }
    

    运行结果:

    
    Student 被回收了
    虚引用被回收了:java.lang.ref.PhantomReference@1ade6f1
    

    我们简单的分析下代码:

    第一个线程往集合里面塞数据,随着数据越来越多,肯定会发生GC。 第二个线程死循环,从queue里面拿数据,如果拿出来的数据不是null,就打印出来。

    从运行结果可以看到:当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中。

    虚引用有什么用呢?在NIO中,就运用了虚引用管理堆外内存。

    以上就是这篇博客的所有内容了。

    END

    Java面试题专栏

    强软弱虚引用,只有体会过了,才能记住

    我知道你 “在看强软弱虚引用,只有体会过了,才能记住

    原文始发于微信公众号(Java知音):强软弱虚引用,只有体会过了,才能记住

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 强软弱虚引用,只有体会过了,才能记住


     上一篇
    面试官——什么是NIO?NIO的原理是什么机制? 面试官——什么是NIO?NIO的原理是什么机制?
    作者:依本多情 https://blog.csdn.net/qq_36520235/ https://blog.csdn.net/qq_36520235/ NIO和IO到底有什么区别?有什么关系?首先说一下核心区别:
    下一篇 
    JVM 史上最最最完整深入解析(12000 字总结) JVM 史上最最最完整深入解析(12000 字总结)
    作者:Huangy远 segmentfault.com/a/1190000014395186 segmentfault.com/a/1190000014395186 工作之余,想总结一下JVM相关知识