java的四种引用
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可达,判定对象是否存活都和“引用”离不开关系。
在JDK 1.2版之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。
一、强引用
强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
二、软引用
软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
应用场景:缓存。
import java.lang.ref.SoftReference;
/**
* @description: 软引用
* ·软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内
* 存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,
* 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
*
* 应用场景:缓存
*
* @author:weirx
* @date:2021/5/19 16:39
* @version:3.0
*/
public class SoftReferenceTest {
public static void main(String[] args) throws InterruptedException {
SoftReference<byte[]> m = new SoftReference(new byte[1024 * 1024 * 10]);
System.out.println(m.get());
//第一次垃圾回收
System.gc();
Thread.sleep(1000);
System.out.println(m.get());
//分配一个强引用的byte,看看当堆空间不足时,是否会将软引用回收。
// 需要启动时将最大堆内存设置为25M,当内存不足后发生第二次垃圾回收之后在获取软引用就是null。
byte[] bytes = new byte[1024 * 1024 * 12];
System.out.println(m.get());
}
}
测试时需要指定启动内存大小:
-Xmx25m
结果:
[B@6f75e721
[B@6f75e721
null
三、弱引用
弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
应用:ThreadLocal;WeakHashMap。
import java.lang.ref.WeakReference;
/**
* @description: 弱引用
*
* 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只
* 能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只
* 被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
*
* 应用:ThreadLocal
* @author:weirx
* @date:2021/5/19 16:54
* @version:3.0
*/
public class WeakReferenceTest {
public static void main(String[] args) throws InterruptedException {
WeakReference<byte[]> w = new WeakReference<>(new byte[1024 * 1024 * 10]);
System.out.println(w.get());
System.gc();
Thread.sleep(1000);
//只经历一次gc就被回收了
System.out.println(w.get());
}
}
结果:
[B@6f75e721
null
四、虚引用
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。
应用:在jvm中,用来管理直接内存(例如NIO中ByteBuffer申请直接内存allocateDirect)的回收.会将虚引用的回收时间添加到一个队列中。通过检测虚引用可以做一些善后操作。
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 虚引用
* 虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的
* 存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚
* 引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供
* 了PhantomReference类来实现虚引用
*
* 应用:在jvm中,用来管理直接内存(例如NIO中ByteBuffer申请直接内存allocateDirect)的回收.会将虚引用的回收时间添加到一个队列中。
* @author:weirx
* @date:2021/5/19 17:09
* @version:3.0
*/
public class PhantomReferenceTest {
static class M {
int a = 0;
}
static ReferenceQueue referenceQueue = new ReferenceQueue();
public static void main(String[] args) {
PhantomReference phantomReference = new PhantomReference(new M(), referenceQueue);
//通过get页不能获取到
System.out.println(phantomReference.get());
List list = new ArrayList();
//模拟线程,不断增加堆空间占用
new Thread(() -> {
while (true) {
list.add(new byte[1024 * 1024]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(phantomReference.get());
}
}).start();
//模拟垃圾回收线程,可用于做善后处理,比如nio的直接内存
new Thread(() -> {
while (true) {
//当虚引用对象被回收时,会在队列里面增加一个信息,当我们获取到信息就可以对这个引用做善后处理
Reference poll = referenceQueue.poll();
if (poll != null) {
System.out.println("虚引用对象被jvm回收了," + poll);
}
}
}).start();
}
}
结果:
null
null
null
null
null
null
null
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at com.cloud.bssp.reference.PhantomReferenceTest.lambda$main$0(PhantomReferenceTest.java:44)
at com.cloud.bssp.reference.PhantomReferenceTest$$Lambda$1/874088044.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
虚引用对象被jvm回收了,java.lang.ref.PhantomReference@7090cd23