CreateArtTechnology
/ Blog
Login
最新文章
Java
语言相关
库相关
虚拟机相关
CreateArtTechnology
项目搭建
使用的工具
自研的工具
开源工具
ELK
ElasticSearch
Jenkins
Markdown
GraphQL
Arthas
生产工具
Linux
Nginx
VersionControl
Subversion
Git
Redis
Archiva
Maven
Zookeeper
Spring
SpringBoot
MySql
HBase
Cassandra
容器化
Docker
Kubernetes
服务容器化从零开始
未分类笔记
算法相关
概念相关
豆知识
机器学习
机器学习从零开始
Java的强、软、弱、虚引用介绍与分析
27
2019-05-27 12:56:21
Java
语言相关
## Java引用类型 Java引用主要分为4种(其实似乎是5种): - `Strong Reference 强引用`,直接引用 - `Soft Reference 软引用`,间接引用 - `Weak Reference 弱引用`,间接引用 - `Phantom Reference 虚引用`,几乎无引用 - Final引用,这里不介绍 **强引用** ```java Object strongReference = new Object(); ``` 我们平常使用最多的就是强引用。按照JVM规范,在GC时通过可达性分析检测到强引用可达时,这个对象不会被回收。 但是在某些情况下,强引用的这个特性会引起`OutOfMemoryError`,比如一直向集合中添加元素。 **软引用** 软引用对象仅在内存不足时会被回收,JVM保证在抛出`OutOfMemoryError`之前已经将软引用对象全部清理了。 **弱引用** JVM不保证弱引用对象的回收时机,在GC线程发现该对象为弱引用对象时就会回收。 **虚引用** 任何时候都可能会被回收。 Java提供了几种间接引用方式,有什么好处吗?是如何起作用的?我们从源码中一点一点看。 ## Reference和ReferenceQueue **ReferenceQueue** ReferenceQueue本质上是一个单向链表,链表的节点是Reference,并提供了同步非阻塞出队方法`poll()`。 同时,ReferenceQueue提供了一个类似`BlockingQueue`的阻塞获取节点的方法`remove()`。 在Reference类的构造方法中,可以指定一个`ReferenceQueue`。 **Reference** Java的强软弱虚四种中,软、弱、虚引用都是继承`Reference
`抽象类。 核心的几个属性和方法: ```java public abstract class Reference
{ ...... private T referent; // 代管的对象 ...... volatile ReferenceQueue super T> queue; // 关联的通知队列 ...... private static Reference
pending = null; // 静态的待处理链 ...... static boolean tryHandlePending(boolean waitForNotify) {......} // 负责检查pending链等关键操作 ...... public T get() { return this.referent; } // 获取直接引用 ...... } ``` Reference类相当于引用代理,正常情况下我们持有Reference对象的引用,Reference对象持有我们真正使用的对象`T referent`的引用。 Reference提供了几个重要的方法: - `get()` 获取代管对象(referent)的直接引用 - `isEnqueued()` 检查是否还在队列中 - `tryHandlePending()` 如果存在可回收的Reference,则将这个Reference插入到构造方法指定的ReferenceQueue中;否则wait()等待 其中,Reference创建了一个名为**“Reference Handler”**的常驻后台线程,用于将referent已被回收的Reference对象加入其引用通知队列ReferenceQueue。 **Reference中的referent** Reference中的referent会受到GC的特殊对待: - GC检测到`reference.referent(非静态)`引用可达性发生变化时,会将对应的Reference对象挂在`Reference.pending(静态)`链上 **ps.文档未介绍“可达性发生变化”是什么情况,我猜测是处于“仅Reference对象持有直接引用”的状态。这个状态按理说是不能回收的,因为引用分析是可达的,但这里特殊处理,即使可达GC也可以回收。** **综合看Reference和ReferenceQueue** 这两个类形成了一个回收通知机制: 1. 新建一个Reference对象,在构造方法中为其绑定一个**间接引用对象referent**,及一个**回收通知队列referenceQueue** 2. 去除referent的其他直接引用 3. Reference类在应用启动时就创建了**Reference Handler**线程,但`Reference.tryHandlePending`方法发现pending对象为null,线程进入`WAIT`状态,pending保存的是需要回收的Reference链的首个节点 4. GC在回收这个Reference对象的referent时,将其Reference对象挂在Reference.pending链上,即GC会将所有**达到可回收状态的Reference**对象挂到静态的pending链上 5. Reference Handler线程被唤醒,通过`Reference.tryHandlePending`方法发现pending中有内容,将链上的Reference对象插入其构造方法指定的**referenceQueue**中(当然,如果没有指定那么就不会入队) 6. 我们通过referenceQueue的`remove()阻塞`或`poll()非阻塞`方法可以拿到**哪些Reference对象的referent已被回收**,可以进行接下来的善后处理 ![](/img/pic/2019052712443146022_png_1163_991_84588) > 参考资料中的图 **应用场景** - 软引用 官方文档提到,软引用非常适合用于内存敏感的缓存,当内存不足时GC将会回收缓存中的部分内容。 **ps.有[参考资料](https://my.oschina.net/robinyao/blog/829983)提到软引用可能导致频繁Full GC。** - 弱引用 官方文档提到,弱引用适合用于规范化映射(Canonicalizing Mappings),可以理解为一个Map中的KV映射。 - 虚引用 `PhantomReference.get()`方法永远返回null,因此一旦失去直接引用仅保留虚引用,那么就无法再获取这个对象的引用。适合用于监控对象是否被回收。 一个典型的应用就是`WeakHashMap`类。 WeakHashMap中的Entry继承自WeakReference类,其中key就是间接引用中的referent,获取key均是通过Reference对象的`get()`方法,即有可能失效。 另外,WeakHashMap持有一个ReferenceQueue对象,每个Entry都在构造方法中关联了这个queue。 其`expungeStaleEntries()`方法会清理失效的Entry,作为大多数方法的前置操作。 结合上边的回收通知机制等分析,WeakHashMap有这些特点: 1. 使用弱引用作为Entry,referent是Entry的key,并关联了一个内部的ReferenceQueue 2. 每次GC时都会清理掉一部分Entry的referent,key变为null——相当于这些内容在Map中“消失”了,不过value占用的空间还没清理 3. key被清理的Entry,如前所述会进入回收通知队列ReferenceQueue 4. 在绝大部分操作之前会先从回收通知队列中获取失效的Entry,并从Map中真正删除 **回收通知机制的好处** 1. GC虽然做了特殊处理,但是插入链表这个额外的操作相对来说负担是非常小的,对GC影响不大,回收通知机制后续的逻辑其实是Reference Handler线程在做。 2. 使用间接引用后,如何清理被回收的数据及相关数据,或者如何接收被回收通知是个问题。 以WeakHashMap举例,被回收的数据都在ReferenceQueue中,比起遍历整个集合查看哪些数据被回收了,显然使用通知机制仅查这个queue效率要高得多。 ## 参考资料 [JDK源码阅读-Reference](http://imushan.com/2018/08/19/java/language/JDK%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB-Reference/) [Java Reference详解 - robin-yao的个人页面 - OSCHINA](https://my.oschina.net/robinyao/blog/829983) [Java 理论与实践: 用弱引用堵住内存泄漏](https://www.ibm.com/developerworks/cn/java/j-jtp11225/index.html)
发布文章 101
文章被阅读 1817
最近修改
什么是“丝滑”的曲线
2021-12-08 15:19:20
高效空间数据索引R树及其批量加载方法STR简介
2021-09-29 20:33:37
关于分库分表的一些事儿
2021-06-25 11:51:25
获得诺奖的稳定匹配理论之TTC算法与GS算法
2021-03-14 23:04:48
算法小白的机器学习入门实践,从零到上线
2021-01-13 14:28:27
分站宗旨
一站式资料平台,减少重复检索,减少重复采坑。