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 ...
随机推荐
- google搜索技巧汇总
由于不能访问google,可访问ggso.in进行搜索. 简单整理记录一下常用的一些Google搜索技巧:或操作一般搜索时,如果输入多个词,默认是与的关系,如输入词1和词2,即搜索同时包含词1和词2的 ...
- EF6(CodeFirst)+MySql开发遇到的坑
最近一不小心偷个懒就已经过了好几个月了,真是惭愧惭愧,出来混终究是要还的,我还是把”脱坑指南“写完吧,-_-~~.点我打开上篇博客 0x001.架构名”dbo”の殇 坑之首也,当提架构名,在mssql ...
- Wix 安装部署教程(十五) --CustomAction的七种用法
在WIX中,CustomAction用来在安装过程中执行自定义行为.比如注册.修改文件.触发其他可执行文件等.这一节主要是介绍一下CustomAction的7种用法. 在此之前要了解InstallEx ...
- Web 架构师的能力(转)
文/刘如鸿 最近和几个朋友在谈到时下流行的Web 2.0,也提到了其中最重要的角色——架构师.多方各有争执,不外乎是因为背景和视角的缘故,包括架构一词,本身就从建筑学借鉴而来,至于架构师,则可以 简单 ...
- 一道原生js题目引发的思考(鼠标停留区块计时)
我瞎逛个啥论坛,发现了一个题目,于是本着练手的心态就开始写起来了,于是各种问题接踵而至,收获不小. 题目是这样的: Demo: mouseenter与mouseover区别demo 跨浏览器的区块计数 ...
- Microsoft Dynamics CRM 2013 and 2011 Update Rollups and Service Packs
Microsoft Dynamics CRM 2013 BTW: RC stands for Release for Candidate, and RTM stands for Release ...
- 这里有个坑---js日期格式yyyy-MM-dd与yyyy/MM/dd
这里有个坑,---------每一个遇到的坑总结后都是一比财富. 我们写脚本的时候,一般定义一个日期格式会使用“2015-12-21”和“2015/12/21”两种数据格式,由于各取所需日期格式并没有 ...
- Linux 容器技术史话:从 chroot 到未来
Linux 容器是一个在单一 Linux 主机上提供多个隔离的 Linux 环境的操作系统级虚拟技术.不像虚拟机(VM),容器并不需要运行专用的访客(guest)操作系统.容器们共享宿主机的(host ...
- 如何优雅的使用vue+vux开发app -01
如何优雅的使用vue+vux开发app -01 很明显下面是个错误的示范: <!DOCTYPE html> <html> <head> <title>v ...
- Python下划线与命名规范
Python下划线与命名规范 先看结论,节省只想知道答案你的宝贵时间: _xxx 不能用于from module import * 以单下划线开头的表示的是protected类型的变量.即保护类型只能 ...