简介

java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型。java为引用类型专门定义了一个类叫做Reference。Reference是跟java垃圾回收机制息息相关的类,通过探讨Reference的实现可以更加深入的理解java的垃圾回收是怎么工作的。

本文先从java中的四种引用类型开始,一步一步揭开Reference的面纱。

java中的四种引用类型分别是:强引用,软引用,弱引用和虚引用。

强引用Strong Reference

java中的引用默认就是强引用,任何一个对象的赋值操作就产生了对这个对象的强引用。

我们看一个例子:

public class ReferenceUsage {@Testpublic void StrongReference(){Object obj = new Object();   // =直接赋予new出的新对象一个强引用}
}

上面我们new了一个Object对象,并将其赋值给obj,这个obj就是new Object()的强引用。

强引用的特性是只要有强引用存在,被引用的对象就不会被垃圾回收。

软引用Soft Reference

软引用在java中有个专门的SoftReference类型,软引用的意思是只有在内存不足的情况下,被引用的对象才会被回收。

先看下SoftReference的定义:

public class SoftReference<T> extends Reference<T>

SoftReference继承自Reference。它有两种构造函数:

    public SoftReference(T referent) public SoftReference(T referent, ReferenceQueue<? super T> q)

第一个参数很好理解,就是软引用的对象,第二个参数叫做ReferenceQueue,是用来存储封装的待回收Reference对象的,ReferenceQueue中的对象是由Reference类中的ReferenceHandler内部类进行处理的。

我们举个SoftReference的例子:

/*** @Author Duanzh* @Description 软引用测试:内存不足即回收 -Xms10m -Xmx10m -XX:+PrintGC* @Date 2022/4/13 14:42* @Version 1.0*/
public class SoftReferenceTest {public static class User {public User(int id, String name) {this.id = id;this.name = name;}public int id;public String name;@Overridepublic String toString() {return "[id=" + id + ", name=" + name + "] ";}}public static void main(String[] args) {// 创建对象,建立软引用// SoftReference<User> userSoftRef = //        new SoftReference<User>(new User(1, "songhk"));// 上面的一行代码,等价于如下的三行代码User u1 = new User(1, "cakin");SoftReference<User> userSoftRef = new SoftReference<User>(u1);u1 = null;    //取消强引用// 从软引用中重新获得强引用对象System.out.println(userSoftRef.get());// 主动触发gc,此时内存应该足够System.gc();System.out.println("内存足够:After GC:");// 垃圾回收之后获得软引用中的对象System.out.println(userSoftRef.get()); // 由于堆空间内存足够,所以不会回收软引用的可达对象。System.out.println("内存紧张或不足:After GC:");try {// 让系统认为内存资源不够 abyte[] b = new byte[1024 * 1024 * 7];// 让系统认为内存资源紧张 b//byte[] b = new byte[1024 * 7000 - 630 * 1024];} catch (Throwable e) {e.printStackTrace();} finally {// 再次从软引用中获取数据System.out.println(userSoftRef.get()); // 在报 OOM 之前,垃圾回收器会回收软引用的可达对象。}}}

输出结果:

a:(OOM,finally中获取到的值为null)

[id=1, name=cakin]
[GC (System.gc()) [PSYoungGen: 1687K->496K(2560K)] 2092K->1061K(9728K), 0.0013469 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 496K->0K(2560K)] [ParOldGen: 565K->876K(7168K)] 1061K->876K(9728K), [Metaspace: 3159K->3159K(1056768K)], 0.0058966 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
内存足够:After GC:
[id=1, name=cakin]
内存紧张或不足:After GC:
[GC (Allocation Failure) [PSYoungGen: 80K->96K(2560K)] 957K->972K(9728K), 0.0004120 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 96K->32K(2560K)] 972K->908K(9728K), 0.0004279 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(2560K)] [ParOldGen: 876K->833K(7168K)] 908K->833K(9728K), [Metaspace: 3160K->3160K(1056768K)], 0.0067242 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 833K->833K(9728K), 0.0004942 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 833K->817K(7168K)] 833K->817K(9728K), [Metaspace: 3160K->3160K(1056768K)], 0.0078196 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
null
HeapPSYoungGen      total 2560K, used 101K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd196e8,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen       total 7168K, used 817K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 11% used [0x00000000ff600000,0x00000000ff6cc6a8,0x00000000ffd00000)Metaspace       used 3191K, capacity 4556K, committed 4864K, reserved 1056768Kclass space    used 341K, capacity 392K, committed 512K, reserved 1048576K
java.lang.OutOfMemoryError: Java heap spaceat com.tuling._03_list.SoftReferenceTest.main(SoftReferenceTest.java:44)

b:(老年代已99%,内存紧张,但没有OOM,finally中获取到的值为null)

[id=1, name=cakin]
[GC (System.gc()) [PSYoungGen: 1679K->488K(2560K)] 2100K->1121K(9728K), 0.0012731 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 633K->877K(7168K)] 1121K->877K(9728K), [Metaspace: 3159K->3159K(1056768K)], 0.0078880 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
内存足够:After GC:
[id=1, name=cakin]
内存紧张或不足:After GC:
[GC (Allocation Failure) [PSYoungGen: 80K->64K(2560K)] 958K->941K(9728K), 0.0005351 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 64K->96K(2560K)] 941K->973K(9728K), 0.0005417 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 96K->0K(2560K)] [ParOldGen: 877K->813K(7168K)] 973K->813K(9728K), [Metaspace: 3160K->3160K(1056768K)], 0.0081207 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 813K->813K(9728K), 0.0004101 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 813K->797K(7168K)] 813K->797K(9728K), [Metaspace: 3160K->3160K(1056768K)], 0.0068592 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
null
HeapPSYoungGen      total 2560K, used 20K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 1% used [0x00000000ffd00000,0x00000000ffd053c0,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen       total 7168K, used 7167K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 99% used [0x00000000ff600000,0x00000000ffcffeb8,0x00000000ffd00000)Metaspace       used 3167K, capacity 4556K, committed 4864K, reserved 1056768Kclass space    used 338K, capacity 392K, committed 512K, reserved 1048576K

可以看到在内存不足的情况下,SoftReference引用的对象会被回收。

弱引用weak Reference

weakReference和softReference很类似,不同的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足。

同样的WeakReference也有两个构造函数:

     public WeakReference(T referent);public WeakReference(T referent, ReferenceQueue<? super T> q);

含义和SoftReference一致,这里就不再重复表述了。

我们看下弱引用的例子:

    @Testpublic void weakReference() throws InterruptedException {Object obj = new Object();WeakReference<Object> weak = new WeakReference<>(obj);obj = null;log.info("{}",weak.get());System.gc();log.info("{}",weak.get());}

输出结果:

22:58:02.019 [main] INFO com.flydean.WeakReferenceUsage - java.lang.Object@71bc1ae4
22:58:02.047 [main] INFO com.flydean.WeakReferenceUsage - null

结论:只要有gc,就会回收弱引用。

虚引用PhantomReference

PhantomReference的作用是跟踪垃圾回收器收集对象的活动,在GC的过程中,如果发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员自己处理,当程序员调用ReferenceQueue.poll()方法,将引用从ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。

和SoftReference和WeakReference不同的是,PhantomReference只有一个构造函数,必须传入ReferenceQueue:

    public PhantomReference(T referent, ReferenceQueue<? super T> q)

看一个PhantomReference的例子:

@Slf4j
public class PhantomReferenceUsage {@Testpublic void usePhantomReference(){ReferenceQueue<Object> rq = new ReferenceQueue<>();Object obj = new Object();PhantomReference<Object> phantomReference = new PhantomReference<>(obj,rq);obj = null;log.info("{}",phantomReference.get());System.gc();Reference<Object> r = (Reference<Object>)rq.poll();log.info("{}",r);}
}

运行结果:

07:06:46.336 [main] INFO com.flydean.PhantomReferenceUsage - null
07:06:46.353 [main] INFO com.flydean.PhantomReferenceUsage - java.lang.ref.PhantomReference@136432db

我们看到get的值是null,而GC过后,poll是有值的。

因为PhantomReference引用的是需要被垃圾回收的对象,所以在类的定义中,get一直都是返回null:

    public T get() {return null;}

Reference和ReferenceQueue

讲完上面的四种引用,接下来我们谈一下他们的父类Reference和ReferenceQueue的作用。

Reference是一个抽象类,每个Reference都有一个指向的对象,在Reference中有5个非常重要的属性:referent,next,discovered,pending,queue。

private T referent;         /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
volatile Reference next;
transient private Reference<T> discovered;  /* used by VM */
private static Reference<Object> pending = null;

每个Reference都可以看成是一个节点,多个Reference通过next,discovered和pending这三个属性进行关联。

先用一张图来对Reference有个整体的概念:

referent就是Reference实际引用的对象。

通过next属性,可以构建ReferenceQueue。

通过discovered属性,可以构建Discovered List。

通过pending属性,可以构建Pending List。

四大状态

在讲这三个Queue/List之前,我们先讲一下Reference的四个状态:

从上面的图中,我们可以看到一个Reference可以有四个状态。

因为Reference有两个构造函数,一个带ReferenceQueue,一个不带。

Reference(T referent) {this(referent, null);}Reference(T referent, ReferenceQueue<? super T> queue) {this.referent = referent;this.queue = (queue == null) ? ReferenceQueue.NULL : queue;}

对于带ReferenceQueue的Reference,GC会把要回收对象的Reference放到ReferenceQueue中,后续该Reference需要程序员自己处理(调用poll方法)。

不带ReferenceQueue的Reference,由GC自己处理,待回收的对象其Reference状态会变成Inactive。

创建好了Reference,就进入active状态。

active状态下,如果引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。

Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。

Pending状态代表等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中。

入Queue的对象,其状态就变成了Enqueued。

Enqueued状态的对象,如果调用poll方法从ReferenceQueue拿出,则该Reference的状态就变成了Inactive,等待GC的回收。

这就是Reference的一个完整的生命周期。

三个Queue/List

有了上面四个状态的概念,我们接下来讲三个Queue/List:ReferenceQueue,discovered List和pending List。

ReferenceQueue在讲状态的时候已经讲过了,它本质是由Reference中的next连接而成的。用来存储GC待回收的对象。

pending List就是待入ReferenceQueue的list。

discovered List这个有点特别,在Pending状态时候,discovered List就等于pending List。

在Active状态的时候,discovered List实际上维持的是一个引用链。通过这个引用链,我们可以获得引用的链式结构,当某个Reference状态不再是Active状态时,需要将这个Reference从discovered List中删除。

WeakHashMap

最后讲一下WeakHashMap,WeakHashMap跟WeakReference有点类似,在WeakHashMap如果key不再被使用,被赋值为null的时候,该key对应的Entry会自动从WeakHashMap中删除。

我们举个例子:

    @Testpublic void useWeakHashMap(){WeakHashMap<Object, Object> map = new WeakHashMap<>();Object key1= new Object();Object value1= new Object();Object key2= new Object();Object value2= new Object();map.put(key1, value1);map.put(key2, value2);log.info("{}",map);key1 = null;System.gc();log.info("{}",map);}

输出结果:

[main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc, java.lang.Object@11028347=java.lang.Object@1f89ab83}
[main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc}

可以看到gc过后,WeakHashMap只有一个Entry了。

一文读懂java中的Reference和引用类型相关推荐

  1. java中date类型如何赋值_一文读懂java中的Reference和引用类型

    简介 java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型.java为引用类型专门定义了一个类叫做Reference.Reference是跟jav ...

  2. 一文读懂Java中File类、字节流、字符流、转换流

    一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...

  3. 一文读懂密码学中的证书

    一文读懂密码学中的证书 之前的文章中,我们讲到了数字签名,数字签名的作用就是防止篡改和伪装,并且能够防止否认.但是要正确运用数字签名技术还有一个非常大的前提,那就是用来验证签名的公钥必须真正的属于发送 ...

  4. JVM(一)一文读懂Java编译全过程

    一文读懂Java编译全过程 java代码首先要通过前端编译器编译成.class字节码文件,然后再按一定的规则加载到JVM(java 虚拟机)内运行,有三种运行方式,解释模式(javac).编译模式(C ...

  5. 一文读懂机器学习中奇异值分解SVD

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 目录: 矩阵分解 1.1 矩阵分解作用 1.2 矩阵分解的方法一文 ...

  6. 一文读懂信息安全中的恶意代码、病毒、木马、蠕虫......

    一文读懂信息安全中的恶意代码.病毒.木马.蠕虫...... 病毒:破坏计算机功能或数据,以破坏为主,传染其他程序的方式是通过修改其他程序来把自身或其变种复制进去完成的,典型的熊猫烧香 蠕虫:通过网络的 ...

  7. 一文读懂机器学习中的模型偏差

    一文读懂机器学习中的模型偏差 http://blog.sina.com.cn/s/blog_cfa68e330102yz2c.html 在人工智能(AI)和机器学习(ML)领域,将预测模型参与决策过程 ...

  8. deque stack java_一文弄懂java中的Queue家族

    简介 java中Collection集合有三大家族List,Set和Queue.当然Map也算是一种集合类,但Map并不继承Collection接口. List,Set在我们的工作中会经常使用,通常用 ...

  9. 一文读懂SpringBoot中的事件机制

    一文读懂SpringBoot中的事件机制?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法. 要"监听"事件,我们总是 ...

最新文章

  1. 锁的算法,隔离级别的问题
  2. 皮一皮:我也想做这样的房东,善解人意、为他人着想...
  3. android android:process=,Android app启动流程
  4. C学习杂记(七)extern声明可省略变量类型
  5. Python爬虫入门五URLError异常处理
  6. kafka集群 kubernetes_为什么 Kubernetes 如此受欢迎?
  7. 2018-2019-1 20165221 《信息安全系统设计基础》第一周学习总结
  8. 软件测试技术第一次试验之——JUnit的安装与使用
  9. 【符号修改】之根据静态库符号以#define的形式重命名
  10. [转载]Spring Cloud微服务Sentinel+Apollo限流、熔断实战
  11. 扇贝有道180628每日一句
  12. php开发完整教程 pdf,PHP完整教程.pdf
  13. java swing浏览器_浏览器控件JxBrowser Swing开发者快速入门指南
  14. java中拦截器和过滤器详解
  15. 全球及中国焦磷酸钠行业“十四五”规划建议与创新环境分析报告2022-2028年版
  16. OpenCV把彩色图片转换为灰度图片
  17. 2023秋招--梦加网络--游戏客户端--二面面经
  18. 【luogu P5320】勘破神机(数学)(数列特征方程)(第一类斯特林数)
  19. VirtualBox 6.0.10
  20. php 提取视频中的声音,怎么提取视频的声音 提取视频中的声音

热门文章

  1. 计算机网络基础心得体会结尾,学习《计算机网络基础知识》心得体会
  2. 2020.5.31 牛客“科林明伦杯” A.点对最大值【树形dp】
  3. 有u盘图标但是在我的电脑里面看不到u盘结局办法—devmgmt.msc
  4. 页面验证是否是真实有效的身份证号码
  5. 【python 笔记/小白快速入门python】python浅谈(一)犹抱琵琶半遮面
  6. H5 捕鱼游戏搭建教程
  7. html 数字变成图片,从100到1000数字表图片
  8. 计算机毕业设计Python+uniapp扫码点餐微信小程序(小程序+源码+LW)
  9. Unity开发VR项目——问题集锦
  10. 人工智能入门教材,我来推荐