本篇文章是对WeakHashMap与散列表的区别进行了详细的分析介绍,需要的朋友参考下
WeakHashMap,此种地图的特点是,当除了自身有对键的引用外,此键没有其他引用那么此地图会自动丢弃此值,见实例:此例子中声明了两个地图对象,一个是散列表,一个是WeakHashMap,同时向两个地图中放入甲、乙两个对象,当散列表移除掉a并且将甲、乙都指向空时,WeakHashMap中的a将自动被回收掉。出现这个状况的原因是,对于a对象而言,当散列表移除掉并且将a指向空后,除了WeakHashMap中还保存a外已经没有指向a的指针了,所以WeakHashMap会自动舍弃掉一,而对于b对象虽然指向了空,但散列表中还有指向b的指针,所以WeakHashMap将会保留复制代码代码如下:包测试;
导入Java。util。hashmap导入Java。util。迭代器;导入Java。util。地图;导入Java。util。弱散列表;
公共类测试{公共静态void main(String[]args)引发异常{ String a=新字符串(' a ');字符串b=新字符串(' b ');map弱map=new weak hashmap();map map=new HashMap();map.put(a,' AAA ');map.put(b,' BBB ');
weakmap.put(a,' AAA ');weakmap.put(b,' BBB ');
地图。删除(a);
a=空;b=空;
系统。GC();迭代器i=map.entrySet().迭代器();while (i.hasNext()) { Map .Entry en=(地图. entry)I . next();系统。出去。println(' map:' en。getkey()':' en。getvalue());}
迭代器j=weakmap.entrySet().迭代器();while (j.hasNext()) { Map .Entry en=(地图. entry)j . next();系统。出去。println('弱映射:' en。getkey()':' en。getvalue());
} }
}我们先明确一下问题:WeakHashMap主要用于移除其内部的expungeStaleEntries,以达到自动释放内存的目的。基本上只要访问WeakHashMap的内容,就会调用这个函数清除内部不再被外部引用的条目。但是如果前期老师变成了WeakHashMap,并且在GC之前从未访问过WeakHashMap,就不能释放内存吗?两个对应的测试用例:weakashmaptest 1:复制代码如下:public类weakashmaptest 1 { public static void main(string[]args)ThrowsException { listweakhashmapbyte[][],byte[][]maps=new ArrayListWeakHashMapbyte[],byte[][]();for(int I=0;i 1000I){ weakashmapbyte[][],byte[][]d=new weakashmapbyte[][],byte[][]();d.put(新字节[1000][1000],新字节[1000][1000]);maps . add(d);system . GC();system . err . println(I);}}}因为Java默认的内存是64M,如果不改变内存参数,测试几个周期后就会溢出。果不其然,WeakHashMap此时并没有自动为我们释放未使用的内存。Weashmaptest2:复制代码代码如下:公共类weakashmaptest 2 { public static void main(string[]args)throws exception { listweakhashmapbyte[][],byte[][]maps=new ArrayListWeakHashMapbyte[],byte[][]();for(int I=0;i 1000I){ weakashmapbyte[][],byte[][]d=new weakashmapbyte[][],byte[][]();d.put(新字节[1000][1000],新字节[1000][1000]);maps . add(d);system . GC();system . err . println(I);for(int j=0;j I;j){ system . err . println(j ' size ' maps . get(j))。size());}}}}这个测试输出正常,不存在内存溢出问题。总结一下:WeakHashMap在你做任何事情的时候都不会自动释放内部未使用的对象,但是当你访问它的内容的时候,释放内部未使用的对象的问题就清楚了。现在我们来整理一下。明白其中的奥妙。weakashmap因为其EntryK实现了弱引用,V是从Weakreference继承的,写在WeakHashMap $ EntryK的类定义和构造函数中,V:复制代码如下:私有静态类EntryK,V扩展weak reference实现Map。Entryk,V Entry(K key,V value,ReferenceQueueK queue,int hash,EntryK,V next) { super(key,queue);this.value=valuethis.hash=hashthis.next=next}请注意,它构造了父类的语句:“super(key,queue);”,传入的是键,所以键是弱引用的,值直接在System.gc()中的this.value中被强引用,键中的字节数组被回收,但值保留(值强链接到条目,条目又链接到map,map链接到arrayList。).如何证明key中的字节是循环使用的?可以通过内存溢出时导出的内存镜像来分析,也可以通过下面的小测试得出结论:用一个小对象替换上面的值,复制代码如下:for(int I=0;我10000;I){ weakashmapbyte[][],Object d=new weakashmapbyte[][],Object();d.put(new byte[1000][1000],new Object());maps . add(d);system . GC();system . err . println(I);}以上代码,即使执行一万次也没有问题,证明key中的字节数组确实被回收了。每次在for循环中,都会创建一个新的WeakHashMap。put操作后,虽然WeakReference的key中的字节数组被GC收集,事件被通知给ReferenceQueue,但是并没有相应的动作触发WeakHashMap处理ReferenceQueue。所以WeakReference包装的键在WeakHashMap中还是存在的,它对应的值也是。
那value是何时被清除的呢?两个例子的分析表明,maps.get(j)。例2中的size()触发了值的恢复,那么它是如何触发的呢?查看WeakHashMap的源代码,可以看到size方法调用了expungeStaleEntries方法,该方法遍历要由vm恢复的条目(在Quene中)。并且条目的值被设置为空,并且存储器被恢复。所以效果是在GC中清除了key,清除key后access WeakHashMap的值也被清除。问题:quene,poll键和map的quene是一样的,poll操作会减少一个引用。问题是,如果先清除了密钥,那么当expungeStaleEntries遍历quene时,还能取出恢复的密钥对应的条目吗?请参见弱引用reference queneweakhashmappublic类weakhashmapk,v扩展抽象mapk,vimplements mapk,v使用弱键的基于哈希表的映射,了解在执行系统时如何回收键中的字节数据。在WeakHashMap中,当一个键不再被正常使用时,它的条目会被自动删除。更准确地说,对于给定的键,其映射的存在并不阻止垃圾收集器丢弃该键,这使得该键可终止、终止然后回收。当一个键被删除时,它的条目被有效地从MAP中删除,所以这个类的行为不同于其他Map实现。支持空值和空键。这个类具有与HashMap类相似的性能特征,并且具有相同的性能参数、初始容量和加载因子。像大多数集合类一样,这个类是异步的。您可以使用Collections.synchronizedMap方法来构造同步WeakHashMap。这个类主要用于这样的键对象,它的equals方法使用==操作符来测试对象标识。一旦这个密钥被丢弃,就不能再被创建。所以过一段时间就不可能在WeakHashMap里找到这个键了,所以不要惊讶它的物品被移除了。该类非常适合用于其equals方法不基于对象标识的键对象,例如String instance。但是对于这个重新创建的key对象,如果丢弃了这个key,WeakHashMap条目就会被自动删除,这就比较混乱了。WeakHashMap类的行为部分依赖于垃圾收集器的操作,因此一些常见的(尽管不是必需的)Map常量不支持该类。因为垃圾收集器可以随时丢弃这个键,所以WeakHashMap就像一个未知的线程,它的条目已经被悄悄地删除了。特别是,即使WeakHashMap实例是同步的,并且没有调用赋值方法,size方法也可能在一段时间后返回一个较小的值。对于isEmpty方法,它可能先返回false,然后返回true。对于给定的键,containsKey方法可能返回true,然后返回false。对于给定的键,get方法可能会返回值,但随后会返回null。对于之前出现在map中的键,put方法返回null,remove方法返回false。对于键集、值集和项集,生成的元素数量越来越少。WeakHashMap中的每个键对象都被间接存储为弱引用指示器对象。因此,无论是在映射内还是映射外,只有在垃圾收集器清除了某个键的弱引用之后,该键才会被自动移除。实现注意:WeakHashMap中的value对象由普通的强引用保存。因此,应该注意确保值对象不会直接或间接强引用自己的键,因为这将防止键被丢弃。注意,value对象可以通过WeakHashMap本身间接引用其对应的key也就是说,值对象可以强烈地引用某个其他键对象,并且与该键对象相关联的值对象将强烈地引用第一个值对象的键。处理这个问题的一种方法是在插入之前将值本身包装在WeakReferences中,比如:m.put (key,new weak reference (value)),然后用get解包。这个类的所有“集合视图方法”返回的迭代器很快失败:迭代器创建后,如果映射在结构上被修改,迭代器将抛出ConcurrentModificationException,除非它在任何其他时间以任何方式通过迭代器自己的remove或add方法被修改。
所以,面对并发修改,迭代器很快就会彻底失效,而不是在未来不确定的时间冒任何不确定行为的风险。注意,迭代器的快速失败行为是不能保证的。一般来说,当存在不同步的并发修改时,不可能做出任何确定的保证。快速迭代器未能尽力抛出ConcurrentModelationException。因此,编写依赖于这个异常的程序是错误的。正确做法是:迭代器的快速失败行为应该仅用于检测 bug。 注1:支持空值和空键。2:它不是线程安全的。注意:不能保证迭代器的快速失败行为。4: WeakHashmap出现故障。注意:确保值对象没有直接或间接地强烈引用它自己的键,因为这将防止键被丢弃。但是value对象可以通过WeakHashMap本身间接引用其对应的key也就是说,一个值对象可能会强引用另一个键对象,与这个键对象关联的值对象会强引用第一个值对象的键,这样就形成了一个循环。处理这个问题的一种方法是在插入之前将值本身包装在WeakReferences中,比如:m.put (key,new weak reference (value)),然后用get解包。例1。例1:复制代码如下:导入Java . lang . ref . weak reference;导入Java . util . linked hashmap;导入Java . util . map;导入Java . util . random;导入Java . util . weak hashmap;import Java . util . concurrent . concurrentlinkedqueue;public class Test {/* * * * @ param args */public static void main(String[]args){ WeakHashMapInteger,WeakReferencePeople map=new WeakHashMapInteger,WeakReferencePeople();人p1=新人('罗宾',1,28);人p2=新人('罗宾',2,29);人p3=新人('哈里',3,30);map.put(new Integer(100),new weak reference people(P1));map.put(new Integer(101),new weak reference people(p2));map.put(new Integer(1),new weak reference people(P3));
for(WeakReferencePeople RP:map。values()){ People p=RP。get();系统。出去。println(' people:' p ');} } }类人{ String name int id int age public People(String name,int id){ this(name,id,0);}public People(String name,int id,int age){ this。name=名称;this.id=idthis.age=年龄;} public String toString(){ return id name age;} public boolean等于(Object o){ if(o==null)返回错误的如果(!(人的实例)返回错误的人民p=(人民)o;布尔型RES=名称。equals(p . name);中频系统。出去。println(' name ' name '是double’);else系统。出去。println(name ' vS ' p . name);返回RES } public int hashCode(){ return name。hashCode();}}
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。