文档章节

四种引用类型、ReferenceQueue和WeakHashMap

城固如春
 城固如春
发布于 2016/10/21 21:43
字数 1601
阅读 14
收藏 0

一、前言                            

  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 = 0; i < 1000; i++) {
            WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
            d.put(new byte[1000][1000], new byte[1000][1000]);
            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 = 0; i < 1000; i++) {  
            WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();  
            d.put(new byte[1000][1000], new byte[1000][1000]);  
            maps.add(d);  
            System.gc();  
            System.err.println(i);  
  
            for (int j = 0; 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/

本文转载自:http://www.cnblogs.com/fsjohnhuang/p/4268411.html

城固如春
粉丝 12
博文 187
码字总数 25379
作品 0
杭州
程序员
私信 提问
四种引用类型、ReferenceQueue和WeakHashMap

一、前言                             JDK1.2以前只提供一种引用类型——强引用 。而JDK1.2后我们多另外的三个选择分别是软引用 、弱引用 和虚引用 。下面将记...

灰鳖子
2016/12/14
18
0
Java弱引用与WeakHashMap

在《Effective Java 2nd Edition》中,第6条“消除过期的对象引用”提到,虽然Java有 垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如的对象池、缓存中的过期对象都...

zray4u
2016/09/04
73
0
重识java-WeakHashMap

四种引用 强引用(StrongReference) 强引用是使用最普遍的引用,平时我们常写的就是强引用 不会回收强引用,即使内存不足的情况下也不会,宁可 软引用(SoftReference) 的主要特点是具有较强的...

hgfgoodcreate
2016/03/10
43
0
WeakHashMap垃圾回收原理

WeakHashMap垃圾回收原理 简书 涤生。 转载请注明原创出处,谢谢! 如果读完觉得有收获的话,欢迎点赞加关注。 介绍 WeakHashMap自然联想到的是HashMap。确实,WeakHashMap与HashMap一样是个...

涤生_YinQi
2017/11/25
0
0
Understanding Weak References

以前我招聘过 高级java工程师,其中一个面试题目是“你对weak reference了解多少?”。这个话题比较偏,不指望每个人都能清楚它的细节。如果面试的人说“Umm...好像和gc(垃圾回收)有点关系...

fjabing
2014/02/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Tedis:基于 TiKV 构建的 NoSQL 数据库

作者介绍: 陈东明,饿了么北京技术中心架构组负责人,负责饿了么的产品线架构设计以及饿了么基础架构研发工作。曾任百度架构师,负责百度即时通讯产品的架构设计。具有丰富的大规模系统构 ...

TiDB
18分钟前
0
0
linux命令

ls命令是linux下最常用的命令。ls命令就是list的缩写,缺省下ls用来打印出当前目录的清单。如果ls指定其他目录,那么就会显示指定目录里的文件及文件夹清单。 通过ls 命令不仅可以查看linux文件...

WinkJie
25分钟前
0
0
你需要的物流运输类报表,这里都有

你需要的物流运输类报表,都在这里 葡萄城报表模板库是一款免费的报表制作、学习和参考工具,包含了超过 200 张高质量报表模板,涵盖了 16 大行业和 50 多种报表类型,为 30 余万报表开发者提...

葡萄城技术团队
32分钟前
1
0
像Java SE一样编写Java EE(ddd探索)

今天主要改写昨天的组合模式成Web系统。 容器接口为 public interface TreeProduct { /** * 展示所有产品 * @return */ List<TreeProduct> allProducts();...

算法之名
33分钟前
0
0
Django Model 模型建立

Django Model 模型 Django Model层是Django的数据模型层,每一个Model类就是数据库中的一张表; 我们需要注意下面几点: model一般都是定义在不同的APP的models.py模块文件中,可以是一个,也...

彩色泡泡糖
42分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部