Java魔法堂:四种引用类型、ReferenceQueue和WeakHashMap
一、前言
JDK1.2以前只提供一种引用类型——强引用 Object obj = new Object(); 。而JDK1.2后我们多另外的三个选择分别是软引用 java.lang.ref.SoftReference 、弱引用 java.lang.ref.WeakReference 和虚引用 java.lang.ref.PhantomReference 。下面将记录对它们和相关连的引用队列 java.lang.ref.ReferenceQueue 和 java.util.WeakHashMap 的学习笔记。
二、四种引用类型
1. 强引用(Strong Reference)
最常用的引用类型,如Object obj = new Object(); 。只要强引用存在则GC时则必定不被回收。
2. 软引用(Soft Reference)
用于描述还游泳但非必须的对象,当堆将发生OOM(Out Of Memory)时则会回收软引用所指向的内存空间,若回收后依然空间不足才会抛出OOM。
一般用于实现内存敏感的高速缓存。
示例:实现学生信息查询操作时有两套数据操作的方案
一、将得到的信息存放在内存中,后续查询则直接读取内存信息;(优点:读取速度快;缺点:内存空间一直被占,若资源访问量不高,则浪费内存空间)
二、每次查询均从数据库读取,然后填充到TO返回。(优点:内存空间将被GC回收,不会一直被占用;缺点:在GC发生之前已有的TO依然存在,但还是执行了一次数据库查询,浪费IO)
通过软引用解决:
ReferenceQueue q = new ReferenceQueue(); // 获取数据并缓存
Object obj = new Object();
SoftReference sr = new SoftReference(obj, q); // 下次使用时
Object obj = (Object)sr.get();
if (obj == null){
// 当软引用被回收后才重新获取
obj = new Object();
} // 清理被收回后剩下来的软引用对象
SoftReference ref = null;
while((ref = q.poll()) != null){
// 清理工作
}
3. 弱引用(Weak Reference)
发生GC时必定回收弱引用指向的内存空间。
4. 虚引用(Phantom Reference)
又称为幽灵引用或幻影引用,,虚引用既不会影响对象的生命周期,也无法通过虚引用来获取对象实例,仅用于在发生GC时接收一个系统通知。
那现在问题来了,若一个对象的引用类型有多个,那到底如何判断它的可达性呢?其实规则如下:
1. 单条引用链的可达性以最弱的一个引用类型来决定;
2. 多条引用链的可达性以最强的一个引用类型来决定;

我们假设图2中引用①和③为强引用,⑤为软引用,⑦为弱引用,对于对象5按照这两个判断原则,路径①-⑤取最弱的引用⑤,因此该路径对对象5的引用为软引用。同样,③-⑦为弱引用。在这两条路径之间取最强的引用,于是对象5是一个软可及对象(当将要发生OOM时则会被回收掉)。
软引用、弱引用和虚引用均为抽象类 java.lang.ref.Reference 的子类,而与引用队列和GC相关的操作大多在抽象类Reference中实现。
三、引用队列(java.lang.ref.ReferenceQueue)
引用队列配合Reference的子类等使用,当引用对象所指向的内存空间被GC回收后,该引用对象则被追加到引用队列的末尾(源码中 boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */ 说明只供Reference实例调用,且仅能调用一次)。引用队列有如下实例方法:
Reference<? extends T> ReferenceQueue#poll() ,从队列中出队一个元素,若队列为空则返回null。
Reference<? extends T> ReferenceQueue#remove() ,从队列中出队一个元素,若没有则阻塞直到有元素可出队。
Reference<? extends T> ReferenceQueue#remove(long timeout) ,从队列中出队一个元素,若没有则阻塞直到有元素可出队或超过timeout指定的毫秒数(由于采用wait(long timeout)方式实现等待,因此时间不能保证)。
四、 java.lang.ref.Reference
Reference内部通过一个 {Reference} next 的字段来构建一个Reference类型的单向链表。另外其内部还包含一个 ReferenceQueue<? super T> queue 字段存放引用对象对应的引用队列,若Reference子类构造函数中没有指定则使用ReferenceQueue.NULL,也就是说每个软、弱、虚引用对象必定与一个引用队列关联。
Reference还包含一个静态字段 {Reference} pending (默认为null),用于存放被GC回收了内存空间的引用对象单向链表。Reference通过静态代码块启动一个优先级最高的守护线程检查pending字段为null,若不为null则沿着单向链表将引用对象追加到该引用对象关联的引用队列当中(除非引用队列为ReferenceQueue.NULL)。守护线程的源码如下:
public void run() {
for (;;) {
Reference r;
synchronized (lock) {
// 检查pending是否为null
if (pending != null) {
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null : rn;
r.next = r;
} else {
try {
// pending为null时,则将当前线程进入wait set,等待GC执行后执行notifyAll
lock.wait();
} catch (InterruptedException x) { }
continue;
}
}
// Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
// 追加到对应的引用队列中
ReferenceQueue q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
}
}
注意:由于通过静态代码块进行线程的创建和启动,因此Reference的所有子类实例均通过同一个线程进行向各自的引用队列追加引用对象的操作。
五、java.util.WeakHashMap
由于WeakHashMap的键对象为弱引用,因此当发生GC时键对象所指向的内存空间将被回收,被回收后再调用size、clear或put等直接或间接调用私有expungeStaleEntries方法的实例方法时,则这些键对象已被回收的项目(Entry)将被移除出键值对集合中。
下列代码将发生OOM
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = ; i < ; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[][], new byte[][]);
maps.add(d);
System.gc();
System.err.println(i);
}
}
而下面的代码因为集合的Entry被移除因此不会发生OOM
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = ; i < ; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[][], new byte[][]);
maps.add(d);
System.gc();
System.err.println(i);
for (int j = ; j < i; j++) {
// 触发移除Entry操作
System.err.println(j+ " size" + maps.get(j).size());
}
}
}
六、总结
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4268411.html ^_^肥仔John
七、参考
《WeakHashMap的神话》http://www.javaeye.com/topic/587995
http://hongjiang.info/java-referencequeue/
Java魔法堂:四种引用类型、ReferenceQueue和WeakHashMap的更多相关文章
- Java中的四种引用类型比较
1.引用的概念 引用这个概念是与JAVA虚拟机的垃圾回收有关的,不同的引用类型对应不同的垃圾回收策略或时机. 垃圾收集可能是大家感到难于理解的较难的概念之一,因为它并不能总是毫无遗漏地解决Java运行 ...
- Java 中的四种引用类型(转)
目录 背景 简介 1. 强引用 StrongReference 2. 弱引用 WeakReference 3. 软引用 SoftReference ...
- java中的四种引用类型
为什么需要引用: Java的内存回收不需要程序员负责,JVM会在必要时启动Java GC完成垃圾回收. Java以便我们控制对象的生存周期,提供给了我们四种引用方式,引用强度从强到弱分别为:强引用.软 ...
- Java中的四种引用类型,强引用,软引用,弱引用,虚引用
对于Java中的垃圾回收机制来说,对象是否被回收的标准在于该对象是否被引用.因此,引用也是JVM进行内存管理的一个重要概念. Java中对象的引用一般有以下4种类型: 1强引用 2软引用 3弱引用 ...
- java中四种引用类型
java中四种引用类型 今天看代码,里面有一个类java.lang.ref.SoftReference把小弟弄神了,试想一下,接触java已经有3年了哇,连lang包下面的类都不了解,怎么混.后来在 ...
- JAVA中的四种引用以及ReferenceQueue和WeakHashMap的使用示例
简介: 本文主要介绍JAVA中的四种引用: StrongReference(强引用).SoftReferenc(软引用).WeakReferenc(弱引用).PhantomReference(虚引用) ...
- 你知道Java的四种引用类型吗
关于java四种引用类型,我也是刚了解,特此记下! 在Java中提供了四个级别的引用:强引用,软引用,弱引用和虚引用.在这四个引用类型中,只有强引用FinalReference类是包内可见,其他三种引 ...
- Java的四种引用类型之弱引用
先说结论: 首先,Java中有四种引用类型:强引用.软引用.弱引用.虚引用.-- 在 Java 1.2 中添加的,见 package java.lang.ref; . 其次,这几个概念是与垃圾回收有关 ...
- java中四种引用类型(对象的强、软、弱和虚引用)
对象的强.软.弱和虚引用在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK 1.2 ...
随机推荐
- 介绍Oedis - Redis OH/RM
作死造轮子 Oedis是近段时间为了解决日志型数据如何与Entity Framework的查询整合的问题写的一个Redis的OH /RM.虽然Redis出来蛮久了,各路高手也都提出了实践方案,但是或许 ...
- Python黑客编程基础3网络数据监听和过滤
网络数据监听和过滤 课程的实验环境如下: • 操作系统:kali Linux 2.0 • 编程工具:Wing IDE • Python版本:2.7.9 • 涉及 ...
- UDP Client—Linux
#include <stdio.h> #include <netinet/ip.h> int main(int argc,char *argv[]) { #define PER ...
- Linq动态条件
很多情况下,我们开发程序,需要动态拼接SQL查询语句; 比如 select top 1 * from User where age= 18 and name = 'renruiquan' 其中红 ...
- 细说.NET中的多线程 (六 使用MemoryBarrier,Volatile进行同步)
上一节介绍了使用信号量进行同步,本节主要介绍一些非阻塞同步的方法.本节主要介绍MemoryBarrier,volatile,Interlocked. MemoryBarriers 本文简单的介绍一下这 ...
- Behind RabbitMQ Exchange Types
what's the underlying philosophy behind "exchange types"? In a word, it is all about imple ...
- C++ 引用
本文主要记录了C++中的引用特性,总结了一下代码示例和错误. 简单的引用 引用是C++的特性,指针是C语言的特性.上面代码结果如下: 20 20 100 20 true 引用和被引用的对象是占用同一个 ...
- js 函数覆盖的问题
今天遇到奇怪问题是,一个html里面引入一个新的js进来后,原来的按钮点击后(假设触发onclick函数)在某个地方卡住了,不往下执行了.—— 之前都是好好的. 调试发现,在onclick中间某一处调 ...
- Node.js与Sails~自定义响应体responses
回到目录 在Node.js里,你可以控制请求和响应,自己可以定义自己的响应方式,如对文本如何响应,对json如何响应,对图像流如何响应等等,而这些在Sails架构里,变得更加容易和清晰了,它位于项目的 ...
- Eclipse 工程迁移到 Android Studio
目标:迁移成功,并成功正常运行! 附加:同步视频在文章后面! 两种方式: 1. 用Gradle导出,在Android Studio中用Gradle导入 (不推荐) 2. 用Android Studio ...