WeakHashMap总结
来源:互联网 发布:远东水利造价软件 编辑:程序博客网 时间:2024/06/08 16:11
WeakHashMap利用WeakReference的弱引用特性让用户在使用的过程中不会因为没有释放Map中的资源而导致内存泄露。WeakHashMap实现了Map接口,使用方式和其他的Map相同,需要注意的是get方法和size方法的使用。在介绍WeakHashMap之前需要先介绍一下Reference的概念。
1、Reference的实现
public abstract class Reference<T> { private T referent; //对应的引用对象 volatile ReferenceQueue<? super T> queue; //引用队列,初始化的时候从外部传入 Reference next; //Reference本身就是链表中的一个节点,next指向队列中的下一个节点 transient private Reference<T> discovered; /* used by VM */ static private class Lock { } private static Lock lock = new Lock(); //用来同步锁操作}
Reference类有四个直接子类,PhantomReference、FinalReference、SoftReference、WeakReference。其中SoftReference比WeakReference约束要强一些,当内存不够用的时候jvm才会将对应引用的对象删除掉,而WeakReference在对象引用不可达的时候就会被jvm清理掉,PhantomReference(幽灵引用)和约束更弱,get方法永远都返回null,无法像前两者一样可以通过get方法获取一个强引用,PhantomReference只能用来观察gc后的引用队列,不能用来获取引用对象。
无论哪种reference,都有一个重要的对象来跟踪对象的gc动作,这个就是ReferenceQueue。
2、ReferenceQueue的实现
public class ReferenceQueue { static private class Lock { }; //锁对象,用来同步队列操作 private Lock lock = new Lock(); private volatile Reference<? extends T> head = null; //头结点 private long queueLength = 0; //队列长度}
a、入队操作
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */ synchronized (lock) { ReferenceQueue<?> queue = r.queue; //获取reference之前的队列,如果没有绑定队列,那就不需要入队 if ((queue == NULL) || (queue == ENQUEUED)) { return false; } assert queue == this; r.queue = ENQUEUED; r.next = (head == null) ? r : head; //把r从链表的头部插入 head = r; //头结点指向r queueLength++; //队列长度+1 if (r instanceof FinalReference) { sun.misc.VM.addFinalRefCount(1); } lock.notifyAll(); //唤醒删除线程删除头结点 return true; }}
出队列的操作相似,每个reference就是链表中的一个节点,next指向下一个reference节点。
b、删除操作
public Reference<? extends T> remove(long timeout) throws IllegalArgumentException, InterruptedException{ if (timeout < 0) { throw new IllegalArgumentException("Negative timeout value"); } synchronized (lock) { Reference<? extends T> r = reallyPoll(); //尝试获取队列的头结点 if (r != null) return r; //获取成功,返回 long start = (timeout == 0) ? 0 : System.nanoTime(); //如果超时时间不为0,就记录一下当前的开始时间 for (;;) { lock.wait(timeout); r = reallyPoll(); if (r != null) return r; if (timeout != 0) { long end = System.nanoTime(); timeout -= (end - start) / 1000_000; //计算一下等待的时间 if (timeout <= 0) return null; //确认等待是否是超时,如果是就返回,否则就认为是有入队请求唤醒当前线程,但是当前线程尝试删除头结点失败了(被其他线程删除了),那么继续尝试删除头结点,再次执行循环中的内容直到超时 start = end; } } }}
ReferenceQueue是线程安全的,出队入队操作都由lock对象来保证线程安全,当用户线程和jvm线程同时访问ReferenceQueue的时候不会出现并发问题。
WeakHashMap
WeakHashMap内部同样是通过一个数组来实现存储,解决冲突的方式也是使用拉链法,weakHashMap中重新定义了Entry类来存储kv键值对,Entry的实现也是实现WeakHashMap特性的关键。
1、Entry的定义
WeakHashMap重新定义了一个entry,这个entry继承了WeakReference类并且实现了Entry接口,使用该Entry存储键值对不会产生强引用,jvm在垃圾回收的时候不会认为该引用是强引用,会正常的回收对象,Entry的定义如下
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; //存储的value final int hash; //hash值 Entry<K,V> next; //拉链法解决冲突,形成单链表 Entry(Object key, V value,ReferenceQueue<Object> queue, int hash, Entry<K,V> next) { super(key, queue); //这里的引用对象是key,跟踪的是key对象的垃圾回收 this.value = value; this.hash = hash; this.next = next; } ...}
Entry的构造器需要传入ReferenceQueue,这个queue就可以用来监控全局的Entry被清理的情况。
2、清理操作
当对象被垃圾回收的时候,当前Map需要删除掉对应的Entry,因为Entry此时指向的对象已经被回收,所以需要找到被jvm回收的对象对应的Entry并且将Entry对象从Map中移除掉,实现方式是expungeStaleEntries方法
private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); //从队列中拿到entry,根据entry获取entry链表在table中的索引 Entry<K,V> prev = table[i]; //找到entry链表的头结点 Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; //找到对应节点 else prev.next = next; e.value = null; // Help GC size--; break; } prev = p; p = next; } } }}
该方法在map中的getTable()、size()和resize()方法中被调用,每次put或是get的时候都会先执行getTable()方法,因此每次读写数据的时候都会清理掉无用的entry,所以用户不会获取到被垃圾回收的清理entry,因此,每次用户调用get方法或是size方法的时候,都会触发清理操作,所以每次返回的结果可能都不相同,因为内部的entry持有的对象已经被jvm回收。
线程安全问题
WeakHashMap作为容器本事不是线程安全的,但是在使用过程中,在单线程环境中ReferenceQueue会被当前业务线程和jvm线程访问,是ReferenceQueue是线程安全的。
- WeakHashMap总结
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- weakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- WeakHashMap
- 织梦dede后台左侧菜单空白不显示解决办法
- mave插件的安装和使用maven创建项目详细解说
- 转载:Spring boot 入门篇(一):构建微服务
- dedecms后台控制常用操作错位解决办法
- 游戏接入微信登录、支付流程2017-12 最新
- WeakHashMap总结
- jdk生成keys(用于配置https)
- 1XML简介
- 使用Mtools分析MongoDB日志文件
- dedecms中列表页显示条数不同的解决方法
- 从前端和后端两个角度分析jsonp跨域访问(完整实例)
- MySQL数据库入门学习(多图预警+新手向~)
- Scrapy框架学习(五)----Request、Response介绍及模拟GitHub登录
- RS485通信字符串数据接收处理的问题