一文读懂java中的Reference和引用类型

简介

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

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

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

强引用Strong Reference

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

我们看一个例子:

public class StrongReferenceUsage {

    @Test
public void stringReference(){
Object obj = new Object();
}
}

上面我们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的例子:

    @Test
public void softReference(){
Object obj = new Object();
SoftReference<Object> soft = new SoftReference<>(obj);
obj = null;
log.info("{}",soft.get());
System.gc();
log.info("{}",soft.get());
}

输出结果:

22:50:43.733 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4
22:50:43.749 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4

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

弱引用weak Reference

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

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

public WeakReference(T referent);

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

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

我们看下弱引用的例子:

    @Test
public 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.pull()方法,将引用出ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。

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

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

看一个PhantomReference的例子:

@Slf4j
public class PhantomReferenceUsage { @Test
public 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;
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中删除。

我们举个例子:

    @Test
public 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了。

总结

本文讲解了4个java中的引用类型,并深入探讨了Reference的内部机制,感兴趣的小伙伴可以留言一起讨论。

本文的例子https://github.com/ddean2009/learn-java-collections

欢迎关注我的公众号:程序那些事,更多精彩等着您!

更多内容请访问 www.flydean.com

一文读懂java中的Reference和引用类型的更多相关文章

  1. 一文读懂Java中的动态代理

    从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...

  2. 一文读懂Java动态代理

    作者 :潘潘 日期 :2020-11-22 事实上,对于很多Java编程人员来说,可能只需要达到从入门到上手的编程水准,就能很好的完成大部分研发工作.除非自己强主动获取,或者工作倒逼你学习,否则我们好 ...

  3. 一文读懂JAVA多线程

    背景渊源 摩尔定律 提到多线程好多书上都会提到摩尔定律,它是由英特尔创始人之一Gordon Moore提出来的.其内容为:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍 ...

  4. 读懂Java中的Socket编程

    Socket,又称为套接字,Socket是计算机网络通信的基本的技术之一.如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的.本文会介绍一下基于TCP/IP的S ...

  5. 读懂Java中的Socket编程(转)

    Socket,又称为套接字,Socket是计算机网络通信的基本的技术之一.如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的.本文会介绍一下基于TCP/IP的S ...

  6. 夯实Java基础系列7:一文读懂Java 代码块和执行顺序

    目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块 ...

  7. 夯实Java基础系列16:一文读懂Java IO流和常见面试题

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  8. 一文读懂Java类加载机制

    Java 类加载机制 Java 类加载机制详解. @pdai Java 类加载机制 类的生命周期 类的加载:查找并加载类的二进制数据 连接 验证:确保被加载的类的正确性 准备:为类的静态变量分配内存, ...

  9. 一文搞懂 Java 中的枚举,写得非常好!

    知识点 概念 enum的全称为 enumeration, 是 JDK 1.5 中引入的新特性. 在Java中,被 enum关键字修饰的类型就是枚举类型.形式如下: enum Color { RED, ...

  10. 一文搞懂--Java中重写equals方法为什么要重写hashcode方法?

    Java中重写equals方法为什么要重写hashcode方法? 直接看下面的例子: 首先我们只重写equals()方法 public class Test { public static void ...

随机推荐

  1. python列表操作的大O效率

  2. 01、NATS基础介绍

    01.NATS基础介绍 项目中使用到了 NATS,但是之前并没有接触过,所以特意去学习了下,网上找了些资料,相对来说,都不是很全的介绍,所以特意记录下笔记,方便自己掌握,也希望可以帮助到其他小伙伴. ...

  3. 用NavigationManager实现订单的列表和新增页面之间的导航

    1.在订单列表页面放置"新增"按钮 2.注入NavigationManager,按钮被点击后导航到新增订单的路径 3.测试一下,能工作了. 4.订单新增后,返回订单列表页面 5.测 ...

  4. TypeScript项目开发运行(即时编译、运行,所见所得)

    1.项目*.ts自动编译 $ tsc . --watch 2.项目本地web服务运行 $ npm install --save-dev webpack-dev-server npm install - ...

  5. 固态硬盘使用f2fs作为根分区安装linux

    目录 前言 碰到的问题 对策 我的实际操作步骤 0.警告 1. 准备 2. 分区 3. 使用网络安装debian10 4. 备份根分区 5. 修改固态硬盘linux根分区为f2fs 6.恢复备份 7. ...

  6. curl比较有用的参数

     精选参数: --include // -i curl的输出中包含http头信息--verbose // 比-i更加丰富,>表示请求的信息, <表示curl接收的信息 *表示curl额外提 ...

  7. Nebula Graph 源码解读系列 | Vol.00 序言

    本文首发于 Nebula Graph Community 公众号 Nebula Graph 是由杭州欧若数网科技有限公司(官网:https://www.vesoft.com/cn/)开源的一款分布式图 ...

  8. [Python] 端口转发代码分享

    我的魔法被公司防火墙限制了,所以我只能让我的代理流量从我自己的服务器过一遍,但是服务器上面的客户端只能允许本机使用,不能开放公网访问,所以就想到了端口转发 但是网络上面找到的端口转发工具需要各种配置, ...

  9. Zabbix“专家坐诊”第185期问答汇总

    问题一 Q:Zabbix5.0版本,如图,请问这里怎么修改回localhost? A:找到文件conf/zabbix.conf.php,改下图这个位置 问题二 Q:大家好,我有个疑问请教下,zabbi ...

  10. 单词本z ctrl shift alt - tr踩踏 shi流出 al不同

    单词本z ctrl shift alt ctrl = control = 控制 con = com = 一起 tr- = 踩 踏 - 原始印欧语形式为 *der- contra = 相对,相反(一起踩 ...