探究Java中的引用
探究Java中的四种引用
从JDK1.2版本开始,Java把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。本篇就来详细探究一下这四种引用的机制:
- 强引用
- 软引用
- 弱引用
- 虚引用
- 详解ReferenceQueue与Reference
强引用
强引用是最普遍的引用,一般通过new关键字来创建出来的对象引用都属于强引用,比如Object o = new Object()。
如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
软引用
在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收;只有在内存空间不足时,软引用才会被垃圾回收器回收。软引用最长被用作内存敏感型的内存缓存。
创建一个软引用的代码示例:
1 |
SoftReference<String> soft = new SoftReference<>("World");
|
SoftReference类的结构如下:
1 |
java.lang.ref.SoftReference#SoftReference(T) java.lang.ref.SoftReference#SoftReference(T, java.lang.ref.ReferenceQueue<? super T>) java.lang.ref.SoftReference#get java.lang.ref.SoftReference#clock java.lang.ref.SoftReference#timestamp |
- 前两个是构造函数,后面会详细介绍ReferenceQueue;
- get方法用于获取这个软引用所指向的对象,如果这个对象已经清除或者被GC收集,那么就返回null;
- clock属性是一个static的long型时间戳,由GC线程进行GC的时候更新;
- timestamp属性也是一个时间戳,每次在调用get方法的时候会对其进行更新,JVM用这个属性用于帮助收集和清理软引用。
弱引用
如果一个对象只具有弱引用,当 JVM 进行垃圾回收时,只要GC线程检测到了,无论当前内存空间是否充足,都会将其回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。弱引用通常用于实现规范化映射。
创建一个弱引用的代码示例:
1 |
WeakReference<String> weakName = new WeakReference<String>("hello");
|
WeakReference类的结构如下:
1 |
java.lang.ref.WeakReference#WeakReference(T) java.lang.ref.WeakReference#WeakReference(T, java.lang.ref.ReferenceQueue<? super T>) |
只是提供了两个构造函数,后一个构造函数传入了一个ReferenceQueue对象。
虚引用
顾名思义,就是形同虚设的引用,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
创建虚引用的代码示例:
1 |
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
|
PhantomReference类的结构如下:
1 |
java.lang.ref.PhantomReference#PhantomReference(T, ReferenceQueue<? super T>) java.lang.ref.PhantomReference#get |
- 只有一个带有ReferenceQueue参数的构造函数,也就是虚引用一定要和引用队列一起使用;
- 同时其get方法也有点特殊,因为虚引用的引用对象相当于没有引用,所以其get方法总是返回null。
详解ReferenceQueue与Reference
引用队列可以与软引用、弱引用以及虚引用一起配合使用,当垃圾回收器准备回收一个对象时,如果发现它还有引用,那么就会在回收对象之前,把这个引用加入到与之关联的引用队列中去。程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收,这样就可以在对象被回收之前采取一些必要的措施。
与软引用、弱引用不同,虚引用必须和引用队列一起使用。
ReferenceQueue实现了一个队列的入队(enqueue)和出队(poll还有remove)操作,内部元素就是Reference。ReferenceQueue名义上是一个队列,但内部并没有实际的存储结构,它的存储是依赖于内部节点之间的关系来实现的。看一下ReferenceQueue实现中用到的属性:
1 |
static ReferenceQueue<Object> NULL = new Null<>();
static ReferenceQueue<Object> ENQUEUED = new Null<>();
static private class Lock { };
private Lock lock = new Lock();
private volatile Reference<? extends T> head = null;
private long queueLength = 0;
|
其实就是一个增加了同步操作的链表的设计,通过head属性来找到链表头,每个链表节点,即Reference对象,都有一个next属性来找到下一个节点。
刚才分析四种引用的时候看到,java.lang.ref.Reference 为 软(soft)引用、弱(weak)引用、虚(phantom)引用的父类。那我们再来看一下Reference类的实现中用到的属性:
1 |
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
volatile Reference next;
transient private Reference<T> discovered; /* used by VM */
static private class Lock { }
private static Lock lock = new Lock();
private static Reference<Object> pending = null;
|
Reference作为ReferenceQueue中的节点,定义了next属性来指向下一个节点,referent为实际指向的对象,pending存储等待被放入ReferenceQueue的引用对象;discovered表示要处理的下一个对象。
Reference类还定义了一个ReferenceHandler线程。
1 |
java.lang.ref.Reference.ReferenceHandler#ReferenceHandler java.lang.ref.Reference.ReferenceHandler#ensureClassInitialized java.lang.ref.Reference.ReferenceHandler#run |
这个线程在Reference类的static的构造块中启动,并且被设置为最高优先级和daemon状态。此线程要做的事情就是不断的检查pending属性是否为null,如果pending不为null,则将pending进行enqueue,否则线程进入wait状态。
由此可见,pending是由jvm来赋值的,当Reference内部的referent对象的可达状态改变时,jvm会将Reference对象放入pending链表。并且这里enqueue的队列是我们在初始化(构造函数)Reference对象时传进来的queue,如果传入了null( 实际使用的是ReferenceQueue.NULL ),则ReferenceHandler则不进行enqueue操作,所以只有非RefernceQueue.NULL的queue才会将Reference进行enqueue。
ReferenceQueue作为 JVM GC与上层Reference对象管理之间的一个消息传递方式,它使得我们可以对所监听的对象引用可达发生变化时做一些处理。
关注我的公众号,获取更多关于面试、技术的文章及福利资源。

探究Java中的引用的更多相关文章
- 初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为
初步探究java中程序退出.GC垃圾回收时,socket tcp连接的行为 今天在项目开发中需要用到socket tcp连接相关(作为tcp客户端),在思考中发觉需要理清socket主动.被动关闭时发 ...
- Java中的引用传递和值传递
Java中的引用传递和值传递 关于Java的引用传递和值传递,在听了老师讲解后,还是没有弄清楚是怎么一回事,于是查了资料,所以在这里与大家分享,有不对的地方,欢迎大家留言. java中是没有指针的,j ...
- Java中没有引用传递只有值传递(在函数中)
◆传参的问题 引用类型(在函数调用中)的传参问题,是一个相当扯的问题.有些书上说是传值,有些书上说是传引用.搞得Java程序员都快成神经分裂了.所以,我们最后来谈一下“引用类型参数传递”的问题. 如下 ...
- Java中弱引用、软引用、虚引用及强引用的区别
Java中弱引用VS软引用 Java中有如下四种类型的引用: 强引用(Strong Reference) 弱引用(WeakReference) 软引用(SoftReference) 虚引用(Phant ...
- Java中的引用与ThreadLocal
Java中的引用--强软弱虚 强引用 Object object = new Object(),这个object就是一个强引用.如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回 ...
- JVM:Java中的引用
JVM:Java中的引用 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 在原来的时候,我们谈到一个类的实例化 Person p = new Person() 在 ...
- 探究Java中的锁
一.锁的作用和比较 1.Lock接口及其类图 Lock接口:是Java提供的用来控制多个线程访问共享资源的方式. ReentrantLock:Lock的实现类,提供了可重入的加锁语义 ReadWrit ...
- 浅谈Java中的引用
在Java语言中,引用是指,某一个数据,代表的是另外一块内存的的起始地址,那么我们就称这个数据为引用. 在JVM中,GC回收的大致准则,是认定如果不能从根节点,根据引用的不断传递,最终指向到一块内存区 ...
- 理解Java中的引用传递和值传递
关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题,有论坛说Java中只有值传递,也有些地方说引用传递和值传递都存在,比较容易让人迷惑.关于值传递和引用传递其实需要分情况看待,今天学习 ...
随机推荐
- TPO1-3Timberline Vegetation on Mountains
At the upper timberline the trees begin to become twisted and deformed. This is particularly true fo ...
- Multiple alleles|an intuitive argument|
I.5 Multiple alleles. 由两个等位基因拓展到多个等位基因,可以得到更多种二倍体基因型: 所以单个等位基因的概率(用i代指某个基因,pi*是该基因的频率)是(以计数的方法表示) 所以 ...
- 上传excel文件,读取内容,增加事务写入数据库
package com.inspur.icpmg.itss.asset.dao.impl; import com.inspur.icpmg.util.DBHelper; import org.apac ...
- mac环境下创建bash_profile文件并写入内容 更改php环境变量
1. 启动终端Terminal 2. 进入当前用户的home目录 输入cd ~ 3. 创建.bash_profile 输入touch .bash_profile 4. 编辑.bash_profile文 ...
- SSID
无线网络中SSID,是路由器发送的无线信号的名字!如果你将你的无线路由器的SSID:命名为:gouwancheng ,那么当你的无线路由器开启,并启用了无线功能,和允许了SSID广播,那么你就可以轻易 ...
- G - 旅行的意义(概率DP) (DAG图的概率与期望)
为什么有人永远渴望旅行,或许就因为,巧合和温暖会在下一秒蜂拥而至吧. 一直想去旅游的天天决定在即将到来的五一假期中安排一场环游世界的旅行.为此,他已经提前查阅了很多资料,并准备画一张旅游路线图.天天先 ...
- 熊海CMS_1.0 代码审计
熊海是一款小型的内容管理系统,1.0版本是多年前的版本了,所以漏洞还是比较多的,而且审计起来难度不大,非常适合入门,所以今天我进行这款cms的代码审计.程序安装后使用seay源代码审计系统打开,首先使 ...
- 让Spring不再难懂-ioc篇
写过java的都知道:所有的对象都必须创建:或者说:使用对象之前必须先创建.而使用ioc之后,你就可以不再手动创建对象,而是从ioc容器中直接获取对象. 就好像我们无需考虑对象的销毁回收一样,因为ja ...
- tomcat打印接口延迟时间
项目中有些页面时延不稳定,需要看每次接口调用时延,怎么看,有两种方法:一种是直接去catalina.out日志中看,一种是直接去localhost_access_log日志中看,第一种需要在代码中实现 ...
- android版本更新框架、新闻客户端、音乐播放器、自定义View、Github客户端、指南针等源码
Android精选源码 XUpdate 一个轻量级.高可用性的Android版本更新框架 Android一个可定制的圆形进度条 Android自定义View分享 打钩动画源码 android音乐文件播 ...