一、前言                            

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的更多相关文章

  1. Java中的四种引用类型比较

    1.引用的概念 引用这个概念是与JAVA虚拟机的垃圾回收有关的,不同的引用类型对应不同的垃圾回收策略或时机. 垃圾收集可能是大家感到难于理解的较难的概念之一,因为它并不能总是毫无遗漏地解决Java运行 ...

  2. Java 中的四种引用类型(转)

    目录 背景 简介          1. 强引用 StrongReference          2. 弱引用 WeakReference          3. 软引用 SoftReference ...

  3. java中的四种引用类型

    为什么需要引用: Java的内存回收不需要程序员负责,JVM会在必要时启动Java GC完成垃圾回收. Java以便我们控制对象的生存周期,提供给了我们四种引用方式,引用强度从强到弱分别为:强引用.软 ...

  4. Java中的四种引用类型,强引用,软引用,弱引用,虚引用

    对于Java中的垃圾回收机制来说,对象是否被回收的标准在于该对象是否被引用.因此,引用也是JVM进行内存管理的一个重要概念. Java中对象的引用一般有以下4种类型: 1强引用  2软引用  3弱引用 ...

  5. java中四种引用类型

    java中四种引用类型  今天看代码,里面有一个类java.lang.ref.SoftReference把小弟弄神了,试想一下,接触java已经有3年了哇,连lang包下面的类都不了解,怎么混.后来在 ...

  6. JAVA中的四种引用以及ReferenceQueue和WeakHashMap的使用示例

    简介: 本文主要介绍JAVA中的四种引用: StrongReference(强引用).SoftReferenc(软引用).WeakReferenc(弱引用).PhantomReference(虚引用) ...

  7. 你知道Java的四种引用类型吗

    关于java四种引用类型,我也是刚了解,特此记下! 在Java中提供了四个级别的引用:强引用,软引用,弱引用和虚引用.在这四个引用类型中,只有强引用FinalReference类是包内可见,其他三种引 ...

  8. Java的四种引用类型之弱引用

    先说结论: 首先,Java中有四种引用类型:强引用.软引用.弱引用.虚引用.-- 在 Java 1.2 中添加的,见 package java.lang.ref; . 其次,这几个概念是与垃圾回收有关 ...

  9. java中四种引用类型(对象的强、软、弱和虚引用)

    对象的强.软.弱和虚引用在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK 1.2 ...

随机推荐

  1. Oracle expdp/impdp导出导入命令及数据库备份

    使用EXPDP和IMPDP时应该注意的事项: EXP和IMP是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用. EXPDP和IMPDP是服务端的工具程序,他们只能在ORACLE服务端使用, ...

  2. 推荐书籍 -《移动App测试的22条军规》

    在今天的博文中,博主希望给大家分享一本博主同事黄勇的最新利作:<移动App测试的22条军规>.黄勇是ThoughtWorks资深敏捷QA和咨询师.对于我来说,和黄勇在一起的工作的这个项目, ...

  3. win10最新预览版9926使用评估

    1月21日微软发布会,终于在周一发布了新的预览版本Build 9926,这个并不是客户预览版,仍然是开发者预览版,如图: 如果要查看自己的windows 10是那个版本,打开运行窗口,输入如下命令: ...

  4. python 对象

    python 对象 在python中,对象就是为C中的结构体在堆上申请的一块内存,一般来说,对象是不能被静态初始化的,并且不能再栈空间上生存.本文主要对Python的基本数据类型做简单的介绍. PyO ...

  5. C#实现的等额本息法、按月付息到期还本法、一次性还本付息法

    你若懂行,那便有用,如下: void Main(){    var x = DengEBenXi.Compute(11111, 12, 3);    x.Dump();    var y = AnYu ...

  6. ASP.NET MVC从视图传递多个模型到Controller

    从后台组织好数据然后传递到页面倒是水到渠成很方便,因为MVC自身就将这样的需求内建到了这个系统中.我只需要在后台组织好一个List 或IEnumerable类型的变量,将需要传递的数据模型扔进去便可. ...

  7. Web语义化

    在昨天和做SEO的同学聊了一会儿,当然我没有学会搜索引擎优化的技巧和知识,但在此之前一直对HTML5中header.footer.sidebar.article等标签嗤之以鼻,觉得这个和div没有什么 ...

  8. PHP面向对象之魔术方法复习

    魔术方法复习 2014-9-2 10:08:00 NotePad++ By jiancaigege 飞鸿影~========================= 1.__construct() 构造方法 ...

  9. Nodejs·构建web应用

    本篇的内容比较多..... 1 首先是从基本的Nodejs服务方面讲述前后端统一语言在web应用中的作用: 2 然后讲了web中基本的知识,从请求方法到路由.从查询字符串到Cookie和Session ...

  10. 《Effective Java》—— 对于所有对象都通用的方法

    本节主要涉及Object中通用的一些方法,比如equals,hashCode,toString,clone,finalize等等 覆盖equals时请遵守通用约定 equals方法实现的等价关系: 自 ...