美文网首页
2020我们来谈谈“强软弱虚”四种引用

2020我们来谈谈“强软弱虚”四种引用

作者: 即将秃头的Java程序员 | 来源:发表于2020-03-09 16:44 被阅读0次

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

Java中的四种引用

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

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

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

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

可以让程序员通过代码的方式来决定某个对象的生命周期;

有利用垃圾回收。

强引用

强引用在 java.lang.ref 中并没有实际的对应类型,但我们程序中,我们写的代码,99.9999%都是强引用:

Object o = new Object();

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

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

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

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

o = null;

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

public classStudent{

    @Override    protectedvoidfinalize()throwsThrowable{

        System.out.println("Student 被回收了");

    }

}

publicstaticvoidmain(String[] args){

        Student student = new Student();

        student = null;

        System.gc();

}

运行结果:

Student被回收了

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

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

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

强引用有如下特点:

强引用可以直接访问目标对象

强引用(存在)指向的对象任何时候都不会被回收,JVM宁愿抛出OOM异常,也不会回收。

强引用可能会导致内存泄漏

注意: 为了尽量避免内存不足的情况,我们可以在变量sb使用后通过显示的将变量sb置为null,来加速对象的回收。

解释: 1. 内存溢出(out of memory) 是指 程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory.

内存泄漏(memory leak) 是指 程序申请内存后,无法释放已申请的内存空间,这样的泄漏积少成多,memory leak 会导致 out of memory .

软引用

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

SoftReferencestudentSoftReference=new SoftReference(new Student());

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

SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());

        Student student = studentSoftReference.get();

        System.out.println(student);

软引用有什么特点呢:

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

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

SoftReference<byte[]> softReference = new SoftReference<byte[]>(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。

软引用也可以和一个引用队列联合使用,如果软引用中的对象(obj)被回收,那么软引用会被 JVM 加入关联的引用队列中。

ReferenceQueue<Object> queue = new ReferenceQueue<>();

Object obj = new Object();

SoftReference softRef = new SoftReference<Object>(obj,queue);

//删除强引用

obj = null;

//调用gc

System.gc();

System.out.println("gc之后的值: " + softRef.get()); // 对象依然存在

//申请较大内存使内存空间使用率达到阈值,强迫gc

byte[] bytes = new byte[100 * 1024 * 1024];

//如果obj被回收,则软引用会进入引用队列

Reference<?> reference = queue.remove();

if (reference != null){

    System.out.println("对象已被回收: "+ reference.get());  // 对象为null

}

引用队列(ReferenceQueue)作用

Queue的意义在于我们在外部可以对queue中的引用进行监控,当引用中的对象被回收后,我们可以对引用对象本身继续做一些清理操作,因为我们引用对象(softRef)也占有一定的资源。

弱引用

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

WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]);

        System.out.println(weakReference.get());

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

WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1]);

        System.out.println(weakReference.get());

        System.gc();

        System.out.println(weakReference.get());

运行结果:

[B@11d7fff

null

可以很清楚的看到明明内存还很充足,但是触发了GC,资源还是被回收了。

弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。

弱引用也可以和一个引用队列联合使用,如果弱引用中的对象(obj)被回收,那么软引用会被 JVM 加入关联的引用队列中。

ReferenceQueue<Object> queue = new ReferenceQueue<>();

Object obj = new Object();

WeakReference weakRef = new WeakReference<Object>(obj,queue);

//删除强引用

obj = null;

System.out.println("gc之后的值: " + weakRef.get()); // 对象依然存在

//调用gc

System.gc();

//如果obj被回收,则软引用会进入引用队列

Reference<?> reference = queue.remove();

if (reference != null){

    System.out.println("对象已被回收: "+ reference.get());  // 对象为null

}

软引用和弱引用都非常适合保存那些可有可无的缓存数据,当内存不足时,缓存数据被回收(再通过备选方案查询),当内存充足时,也可以存在较长时间,起到加速的作用。

应用

WeakHashMap

当key只有弱引用时,GC发现后会自动清理键和值,作为简单的缓存表解决方案。

ThreadLocal

ThreadLocal.ThreadLocalMap.Entry 继承了弱引用,key为当前线程实例,和WeakHashMap基本相同。

虚引用

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

ReferenceQueue queue = new ReferenceQueue();

        PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);

        System.out.println(reference.get());

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

null

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

publicTget(){

        return null;

    }

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

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

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

ReferenceQueue queue = new ReferenceQueue();

        PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);

        System.out.println(reference.get());

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

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

我们来用代码实践下吧:

ReferenceQueue queue = new ReferenceQueue();

        List<byte[]> bytes = new ArrayList<>();

        PhantomReference<Student> reference = new PhantomReference<Student>(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中,就运用了虚引用管理堆外内存。

以上内容都是我自己的一些感想,分享出来欢迎大家指正,顺便求一波关注,有问题或者需要学习资料的伙伴可以点击Java学习分享群一起来聊天哦

相关文章

  • 2020我们来谈谈“强软弱虚”四种引用

    以前学习强软弱虚引用的时候,只是走马观花看看博客,并没有自己写代码去实践、去证明,导致每次看完后,过不了多久就忘了...

  • Java中的四种引用

    1.强软弱虚四种引用 1.1 强引用 执行结果: 解释:强引用是JVM的默认实现,即使内存不足会导致OOM(Out...

  • java四种引用类型

    java中得四种引用 Java中有四种引用类型:强引用、软引用、弱引用、虚引用 强引用我们代码中常写,如: Obj...

  • 强软弱虚引用

    四种引用的区别: 强引用:如果一个对象具有强引用,那垃圾回收器绝不会回收它。软引用:如果一个对象只具有软引用,则内...

  • 强软弱虚引用

    一、强引用(StrongReference) 不会回收有强引用的对象。 除了强引用,其他的引用都需要借助java....

  • 强软弱虚引用

    强引用 简介: 强引用 (Strong Reference) 特征: 只有手动赋值为 null 才会被垃圾回收线程...

  • JAVA四种引用(强引用,弱引用,软引用,虚引用)

    JAVA四种引用(强引用,弱引用,软引用,虚引用) [toc] 参考:Java 的强引用、弱引用、软引用、虚引用四...

  • Java四种引用模式在Android种的应用

    Java中的引用一共有四种:强引用、软引用、弱引用 和 虚引用。下面来分别说一下这四种引用的用法及区别。 强引用(...

  • Java的四种对象引用类型

    Java的四种对象引用类型:强引用、弱引用、软引用、虚引用。 强引用(StrongReference): 强引用是...

  • java四种引用强软弱虚

    强引用:最常见的,不会被GC回收的对象,如 Object obj = new Object(); 软引用:可有可无...

网友评论

      本文标题:2020我们来谈谈“强软弱虚”四种引用

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