《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?

Posted 熊猫IT学院

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?相关的知识,希望对你有一定的参考价值。

1.引言

其实强引用、软引用、弱引用、虚引用这四个概念非常简单好记。

在开头先总结一下这四个引用的特点吧。

强引用:gc时不会回收
软引用:只有在内存不够用时,gc才会回收
弱引用:只要gc就会回收
虚引用:是否回收都找不到引用的对象,仅用于管理直接内存

接下来详细看看这四种引用,结合代码,深刻的体会一下。

2.强引用

即我们平时最常见的:

Object object = new Object();

在这里插入图片描述
只要一个对象有强引用,垃圾回收器就不会进行回收。即便内存不够了,抛出OutOfMemoryError异常也不会回收。

/**
 * 一个对象
 * 重写finalize方法,可以知道已经被回收的状态
 *
 * @author 
 * @date 2020-05-23
 */
public class OneObject {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("啊哦~OneObject被回收了");
    }
}

/**
 * 强引用例子
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowStrongReference {
    public static void main(String[] args) {
        // 直接new一个对象,就是强引用
        OneObject oneObject = new OneObject();
        System.out.println("输出对象地址:" + oneObject);
        System.gc();
        System.out.println("第一次gc后输出对象地址:" + oneObject);
        oneObject = null;
        System.gc();
        System.out.println("置为null后gc输出对象地址:" + oneObject);
    }
}

执行代码,可以看到以下输出:

输出对象地址:com.esparks.pandora.learning.references.OneObject@72ea2f77
第一次gc后输出对象地址:com.esparks.pandora.learning.references.OneObject@72ea2f77
置为null后gc输出对象地址:null
啊哦~OneObject被回收了

3.软引用

需要通过SoftReference对象实现:

SoftReference<OneObject> oneObjectSr = new SoftReference<>(new OneObject());

在这里插入图片描述
当内存足够的时候,垃圾回收器不会进行回收。当内存不够时,就会回收只存在软引用的对象释放内存。

常用于本地缓存处理。

/**
 * 软引用
 * 内存不够了就会回收
 * 注意,运行时需要保证heap大小为35m,即小于实验中全部对象的大小,才能触发gc
 * -Xmx35m
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowSoftReference {
    public static void main(String[] args) {
        // 我们需要通过SoftReference来创建软引用
        SoftReference<OneObject> oneObjectSr = new SoftReference<>(new OneObject());
        // 我们这里创建一个大小为20m的数组
        SoftReference<byte[]> arraySr = new SoftReference<>(new byte[1024 * 1024 * 20]);
        System.out.println("软引用对象oneObjectSr的地址:" + oneObjectSr);
        System.out.println("通过oneObjectSr关联的oneObject对象的地址:" + oneObjectSr.get());
        System.out.println("数组的地址:" + arraySr.get());
        System.gc();
        System.out.println("正常gc一次之后,oneObject对象并没有回收。地址" + oneObjectSr.get());

        // 再创建另一个大小为20m的数组,这样heap就不够大了,从而系统自动gc。如果依旧不够,会把已有的软引用关联的对象都回收掉。
        System.out.println("创建另一个大小为20m的数组otherArray");
        byte[] otherArray = new byte[1024 * 1024 * 20];
        System.out.println("otherArray的地址:" + otherArray);

        // gc后,软引用对象还在,但是通过软引用对象创建的对象就被回收了
        System.out.println("现在srObject的地址:" + arraySr);
        System.out.println("现在srObject中oneObject对象的地址:" + arraySr.get());
        System.out.println("刚才的数组对象也被回收啦,地址:" + arraySr.get());
    }
}

执行代码,可以看到以下输出:

软引用对象oneObjectSr的地址:java.lang.ref.SoftReference@72ea2f77
通过oneObjectSr关联的oneObject对象的地址:com.esparks.pandora.learning.references.OneObject@33c7353a
数组的地址:[B@681a9515
正常gc一次之后,oneObject对象并没有回收。地址com.esparks.pandora.learning.references.OneObject@33c7353a
创建另一个大小为20m的数组otherArray
啊哦~OneObject被回收了
otherArray的地址:[B@3af49f1c
现在srObject的地址:java.lang.ref.SoftReference@19469ea2
现在srObject中oneObject对象的地址:null
刚才的数组对象也被回收啦,地址:null

4.弱引用

需要通过WeakReference对象实现:

WeakReference<OneObject> oneObjectWr = new WeakReference<>(new OneObject());

在这里插入图片描述
只要发生gc,就会回收只存在弱引用的对象。

常用于Threadlocal。

/**
 * 弱引用
 * 只要gc就会回收
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowWeakReference {
    public static void main(String[] args) {
        // 我们需要通过WeakReference来创建弱引用
        WeakReference<OneObject> objectWr = new WeakReference<>(new OneObject());
        System.out.println("弱引用objectWr的地址:" + objectWr);
        System.out.println("弱引用objectWr关联的oneObject对象的地址:" + objectWr.get());

        System.gc();

        // gc后,弱引用对象还在,但是通过弱引用对象创建的对象就被回收了
        System.out.println("gc后,弱引用objectWr的地址:" + objectWr);
        System.out.println("gc后,弱引用objectWr关联的oneObject对象的地址:" + objectWr.get());
    }
}

5.虚引用

需要通过PhantomReference对象和ReferenceQueue实现:

private ReferenceQueue<OneObject> queue = new ReferenceQueue<>();
PhantomReference<OneObject> oneObjectPr = new PhantomReference<>(new OneObject(), queue);

在这里插入图片描述

无论是否gc,其实都获取不到通过PhantomReference创建的对象。

其仅用于管理直接内存,起到通知的作用。

这里补充一下背景。因为垃圾回收器只能管理JVM内部的内存,无法直接管理系统内存的。对于一些存放在系统内存中的数据,JVM会创建一个引用指向这部分内存。
在这里插入图片描述
当这个引用在回收的时候,就需要通过虚引用来管理指向的系统内存。这里还需要依赖一个队列来实现。当触发gc对一个虚引用对象回收时,会将虚引用放入创建时指定的ReferenceQueue中。之后单独对这个队列进行轮训,并做额外处理。
在这里插入图片描述

/**
 * 虚引用
 * 只用于管理直接内存,起到通知的作用
 *
 * @author 
 * @date 2020-05-23
 */
public class ShowPhantomReference {
    /**
     * 虚引用需要的队列
     */
    private static final ReferenceQueue<OneObject> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        // 我们需要通过WeakReference来创建虚引用
        PhantomReference<OneObject> objectPr = new PhantomReference<>(new OneObject(), QUEUE);
        System.out.println("虚引用objectPr的地址:" + objectPr);
        System.out.println("虚引用objectPr关联的oneObject对象的地址:" + objectPr.get());

        // 触发gc,然后检查队列中是否有虚引用
        while (true) {
            System.gc();
            Reference<? extends OneObject> poll = QUEUE.poll();
            if (poll != null) {
                System.out.println("队列里找到objectPr啦" + poll);
                break;
            }
        }
    }
}

执行代码,可以看到以下输出:

虚引用objectPr的地址:java.lang.ref.PhantomReference@72ea2f77
虚引用objectPr关联的oneObject对象的地址:null
啊哦~OneObject被回收了
队列里找到objectPr啦java.lang.ref.PhantomReference@72ea2f77

以上是关于《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?的主要内容,如果未能解决你的问题,请参考以下文章

《我要进大厂系列 六》-谈谈你对ThreadLocal理解?

《我要进大厂系列 八》-谈谈你对Synchronized理解?

《我要进大厂系列 一》-说说你对volatile关键字理解

《我要进大厂系列 九》-谈谈Spring循环依赖

《我要进大厂系列 四》-谈谈你了解的日志框架

《我要进大厂系列 二》-说说你对CAS理解