文章目录

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());

《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?_虚引用_02
当内存足够的时候,垃圾回收器不会进行回收。当内存不够时,就会回收只存在软引用的对象释放内存。

常用于本地缓存处理。

/**
* 软引用
* 内存不够了就会回收
* 注意,运行时需要保证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());

《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?_虚引用_03
只要发生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);

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

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

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

这里补充一下背景。因为垃圾回收器只能管理JVM内部的内存,无法直接管理系统内存的。对于一些存放在系统内存中的数据,JVM会创建一个引用指向这部分内存。
《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?_弱引用_05
当这个引用在回收的时候,就需要通过虚引用来管理指向的系统内存。这里还需要依赖一个队列来实现。当触发gc对一个虚引用对象回收时,会将虚引用放入创建时指定的ReferenceQueue中。之后单独对这个队列进行轮训,并做额外处理。
《我要进大厂系列 七》-谈谈你对强软弱虚四种引用理解?_软引用_06

/**
* 虚引用
* 只用于管理直接内存,起到通知的作用
*
* @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