强引用、软引用、弱引用、虚引用 --《JAVA编程思想》75

今天记录下面试中常常问到的知识点:什么是强引用、弱引用、软引用、虚引用?

1.什么是引用?

首先,我们先来弄清什么是引用。

JAVA 中的一切皆对象,但除了基本类型是直接操作的原始值,其他都是通过引用(Reference)指向的存储了对象基本信息的内存地址。这也解释了我们在打印不同类型的对象时,为什么有的是原始值,有的是内存地址。

    public static void main(String[] args) {
    	//指向内存中的值
        String str = "abc";
  		System.out.println(str);
        //指向内存中的地址
        Object o = new Object();
        System.out.println(o);
    }

abc
java.lang.Object@1b6d3586

一个对象只有被引用和没被引用两种状态,没被引用的对象随时可能会被垃圾回收器进行回收,从而释放内存。

但我们希望某些对象在内存足够时,一直存储在内存之中;内存不够时,可以自动释放;为了让我们自己来控制对象的生命周期,JAVA 提供了四种引用类型,它们的引用强度依次由强至弱,分别为:

  • 强引用
  • 弱引用
  • 软引用
  • 虚引用

接下来和大家一起学习它们具体的使用场景。

2.强引用

强引用为 JAVA 中最常见的引用,即将一个对象赋值给一个引用对象 ,如:Object o = new Object()

当一个对象被引用变量引用时,即使发生内存不足,垃圾回收器也不会回收被强引用指向的对象,故这也是引发内存泄露的主要原因。

如同下例,不断往 LinkedList放入 Integer[] 数组,内存一直得不到释放,最终造成内存泄露。

    public static void main(String[] args) {
        LinkedList<Integer[]> linkedList = new LinkedList<>();
        while (true){
            linkedList.add(new Integer[1024*1024]);
        }
    }
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at mtn.baymax.charpter17.ReferenceTest.main(ReferenceTest.java:15)

3.软引用

软引用需要通过 java.lang.ref.SoftReference来实现,不仅需要指定参数的泛型,还要往构造方法中放入实例对象;SoftReference 可通过 get() 返回存储的对象。

软引用中存储的对象,只有当内存不足时,才会被垃圾回收器清理。

如下述案例:当内存发生泄露时,垃圾回收器进行清理,同时会结束软引用存储对象的生命周期。

    public static void main(String[] args) {
        SoftReference<Object> softReference = new SoftReference<>(new Object());
        System.out.println(softReference.get());
        LinkedList<Integer[]> linkedList = new LinkedList<>();
        try {
            //故意制造内存泄露引发垃圾回收
            while (linkedList.size() < 1024 * 1024) {
                linkedList.add(new Integer[1024 * 1024]);
            }
        } catch (OutOfMemoryError error) {
            System.out.println("发生内存泄露");
        }
        System.out.println(softReference.get());
    }
java.lang.Object@1b6d3586
发生内存泄露
null

软引用适合用于实现内存敏感的高速缓存

4.弱引用

弱引用的生命周期比软引用更短,当垃圾回收器下一次执行清理时,弱引用内存储的对象就会被回收;需要通过 java.lang.ref.WeakReference 来实现,使用方式同 SoftReference 。

如果你有一个对象偶尔会使用,又不想关注它的生命周期,便可以考虑使用 WeakReference 。

    public static void main(String[] args) {
        WeakReference<Object> weakReference = new WeakReference<>(new Object());
        System.out.println(weakReference.get());
        System.gc();
        System.out.println(weakReference.get());
    }
java.lang.Object@1b6d3586
null

5.虚引用

虛引用是引用关系最弱的一种,它随时都有可能被垃圾回收,需要通过 java.lang.ref.WeakReference 来实现,且必须搭配引用队列 java.lang.ref.ReferenceQueue 来使用;

通过查看 WeakReference 的源码,我们发现虚引用在任何时候都是返回 null,如同未持有任何对象;
在这里插入图片描述

那么虚引用有什么意义呢?当虚引用中持有的对象被垃圾回收后,引用队列 ReferenceQueue 会将 WeakReference 加入到其中,我们可以通过这种机制监听对象何时被清理,从而进行下一步操作。

    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(o, referenceQueue);
        System.out.println(o);
        //引用队列此时为空
        System.out.println(referenceQueue.poll());
        //虚引用永远返回null
        System.out.println(phantomReference.get());
        //将强引用释放
        o = null;
        //进行垃圾回收
        System.gc();
        //无法再次获取到object,phantomReference会被自动添加至referenceQueue
        System.out.println(o);
        //返回referenceQueue中的phantomReference
        System.out.println(referenceQueue.poll());
        //phantomReference依旧返回null
        System.out.println(phantomReference.get());
    }
java.lang.Object@1b6d3586
null
null
null
java.lang.ref.PhantomReference@4554617c
null

6.引用队列

引用队列 ReferenceQueue 可以配合软引用、弱引用、虚引用一同使用,虚引用是强制要求使用 ReferenceQueue ,软引用和弱引用则是可选的,当这几类引用中的持有对象被垃圾回收,它们被会自动添加至 ReferenceQueue ,从而达到监听对象回收的效果。

下例展示了 SoftReference和 WeakReference 配合 ReferenceQueue 的工作场景,当引用中的持有对象被回收时,才会被自动加入进 ReferenceQueue 中。

        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        System.out.println("-----演示监听软引用被回收-----");
        SoftReference<Object> softReference = new SoftReference<>(new Object(), referenceQueue);
        System.out.println(referenceQueue.poll());
        System.out.println(softReference.get());
        LinkedList<Integer[]> linkedList = new LinkedList<>();
        try {
            //故意制造内存泄露引发垃圾回收
            while (linkedList.size() < 1024 * 1024) {
                linkedList.add(new Integer[1024 * 1024]);
            }
        } catch (OutOfMemoryError error) {
            System.out.println("发生内存泄露");
            System.out.println(referenceQueue.poll());
        }
        System.out.println(softReference.get());
        System.out.println("-----演示监听弱引用被回收-----");
        WeakReference<Object> weakReference = new WeakReference<>(new Object(), referenceQueue);
        System.out.println(referenceQueue.poll());
        System.out.println(weakReference.get());
        System.gc();
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());
-----演示监听软引用被回收-----
null
java.lang.Object@1b6d3586
发生内存泄露
java.lang.ref.SoftReference@4554617c
null
-----演示监听弱引用被回收-----
null
java.lang.Object@74a14482
null
java.lang.ref.WeakReference@1540e19d

7.参考资料

资料名称链接
阿里面试: 说说强引用、软引用、弱引用、虚引用吧 https://cloud.tencent.com/developer/article/1632413
Java 的强引用、弱引用、软引用、虚引用https://cloud.tencent.com/developer/article/1408097

本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BaymaxCS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值