`

WeakHashMap是如何清除不用的key的

阅读更多

WeakHashMap 是主要通过 expungeStaleEntries 这个函数的来实现移除其内部不用的条目从而达到的自动释放内存的目的的 . 基本上只要对 WeakHashMap 的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了 WeakHashMap ,而在 GC 以前又不曾访问该 WeakHashMap, 那不是就不能释放内存了吗?

对应的两个测试案例 :

WeakHashMapTest1 :

public class WeakHashMapTest1 {
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

);
}
}
}

  

由于 Java 默认内存是 64M ,所以再不改变内存参数的情况下,该测试跑不了几步循环就内存溢出了。果不其然, WeakHashMap 这个时候并没有自动帮我们释放不用的内存。

WeakHashMapTest2 :

public class WeakHashMapTest2 {
    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++) {
                System.err

.println

(j + " size" + maps.get

(j).size

());
            }
        }
    }
} 

这次测试输出正常 , 不在出现内存溢出问题 .

总结来说: WeakHashMap 并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象

问题讲清楚了 , 现在我们来梳理一下 . 了解清楚其中的奥秘 .

WeakHashMap 实现弱引用,是因为它的 Entry<K,V> 是继承自 WeakReference<K>

WeakHashMap$Entry<K,V> 的类定义及构造函数里面是这样写的

private static class Entry<

K,V>

 
    extends WeakReference<

K>

 
    implements Map.Entry

<

K,V>

 Entry(K key, V value, ReferenceQueue<

K>

 queue,int hash, Entry<

K,V>

 next) { 
super(key, queue); 
this.value

 = value;

 
this.hash

 = hash;

 
this.next

 = next;

 
}

请注意它构造父类的语句: “super(key, queue);” ,传入的是 key ,因此 key 才是进行弱引用的, value 是直接强引用关联在 this.value 之中 . System.gc() 时, key 中的 byte 数组进行了回收 , value 依然保持 (value 被强关联到 entry ,entry 又关联在 map ,map 关联在 arrayList .).

如何证明 key 中的 byte 被回收了呢 ? 可以通过内存溢出时导出的内存镜像进行分析 , 也可以通过如下的小测试得出结论 :

把上面的value用小对象代替,

for (int i

 = 0;

 i

 <

 10000;

 i

++) { 
    WeakHashMap<

byte[][], Object>

 d = new WeakHashMap<

byte[][], Object>

(); 
    d.put

(new byte[1000][1000], new Object()); 
    maps.add

(d); System.gc

(); 
    System.err

.println

(i

); 
} 

上面的代码,即使执行10000次也没有问题,证明key中的byte数组确实被回收了。

for 循环中每次都new一个新的WeakHashMap,在put操作后,虽然GC将WeakReference的key中的byte数组回收了,并将事件通 知到了ReferenceQueue,但后续却没有相应的动作去触发 WeakHashMap 去处理 ReferenceQueue

所以 WeakReference 包装的key依然存在在WeakHashMap中,其对应的value也当然存在。

  value 是何时被清除的呢 ?

对两个例子进行分析可知 , 例子二中的maps.get(j).size()触发了 value 的回收 , 那又如何触发的呢 . 查看 WeakHashMap 源码可知 ,size 方法调用了 expungeStaleEntries方法 , 该方法对 vm 要回收的的 entry(quene ) 进行遍历 , 并将 entry value 置空 , 回收了内存 .

所以效果是 key GC 的时候被清除 ,value key 清除后访问 WeakHashMap 被清除 .

疑问 :key quene map quene 是同一个 quene,poll 操作会减少一个 reference, 那问题是 key 如果先被清除 , expungeStaleEntries遍历 quene 时那个被回收的 key 对应的 entry 还能取出来么 ???

关于执行 Sys tem.GC ,key 中的 byte 数据如何被回收了 , 请见 WeakReference referenceQuene

分享到:
评论

相关推荐

    WeakHashMap的使用方法详解

    5. WeakHashMap 的优点:WeakHashMap 可以自动地清除无用的缓存对象,从而避免了内存泄露,并且 WeakHashMap 可以在缓存中的对象变得无用时自动地将其清除。 6. WeakHashMap 的缺点:WeakHashMap 的键对象是弱引用...

    java集合-WeakHashMap的使用

    WeakHashMap是Java中的一种特殊的哈希表实现,它使用弱引用(Weak Reference)来保存键对象。当键对象没有被其他强引用引用时,在垃圾回收时会自动从WeakHashMap中移除对应的键值对。

    Java编程WeakHashMap实例解析

    Java编程WeakHashMap实例解析 WeakHashMap是Java编程中的一种特殊的HashMap实现,它使用弱引用来保存键和值,这样可以使得垃圾回收器自动清理键和值。在WeakHashMap中,键和值都是弱引用的,这样可以避免内存泄露...

    Java弱引用与WeakHashMap

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

    map用法总结

    String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + ": " + value); } ``` 或者,使用传统的迭代器: ```java Iterator, Integer&gt;&gt; it = map.entrySet().iterator(); ...

    弱类型语言允许将一块内存看做多种类型

    - **expungeStaleEntries 方法**:为了确保 WeakHashMap 的键被正确清理,每当访问 WeakHashMap 的时候,都会调用 expungeStaleEntries 方法来清除已经无效的键值对。 #### 七、WeakHashMap 的局限性 - **访问触发...

    线程死锁CPU过高,请求原因分析

    在Java编程中,WeakHashMap是一种特殊的哈希表,它的键(Key)是弱引用,当键被垃圾回收器清除后,即使有值(Value)存在,该条目也会自动从哈希表中移除。 线程死锁的原因通常包括以下几点: 1. **资源互斥**:每...

    比较Vector、ArrayList和hashtable hashmap

    这意味着如果键不再被任何对象引用,那么即使在 WeakHashMap 中,这个键也会被垃圾收集器回收。这种方式可以防止内存泄漏,但也可能导致数据丢失。 总结: - Vector 和 ArrayList 都实现了 List 接口,其中 Vector ...

    Java很好的学习笔记集合Map,学习代码

    5. WeakHashMap:键使用弱引用,当键不再被引用时,键值对会被自动删除。 Map的遍历方式: 1. for-each循环:使用增强for循环遍历Map的entrySet()。 2. Iterator迭代器:通过Map的entrySet().iterator()获取迭代器...

    Map-main-源码.rar

    Map接口是Java集合框架中的重要组成部分,它提供了一种存储键值对数据结构的方式,使得我们可以通过键(Key)快速查找对应的值(Value)。在Java中,有许多实现了Map接口的类,如HashMap、TreeMap、LinkedHashMap等...

    从DELPHI到JAVA转换.docx

    而在Java中,Key/Value存储使用HashMap、TreeMap、WeakHashMap等类,例如:`HashMap,String&gt; map = new HashMap();map.put("key","value");`。需要注意的是,在Java中,Key/Value存储需要提供hashCode函数和equals...

    Java集合框架完整说明便于了解集合

    TreeMap则按照Key进行排序,而WeakHashMap使用弱引用键,有助于防止内存泄漏。 4. ConcurrentHashMap和Hashtable:ConcurrentHashMap是线程安全的Map,采用了分段锁策略,比Hashtable在多线程环境下表现更优。它在...

    Java中List、ArrayList、Vector及map、HashTable、HashMap分别的区别.

    WeakHashMap是一种特殊的Map实现,它使用弱引用作为键,当键不再被引用时,即使在Map中,也会被垃圾回收器清除,从而释放内存资源。 总结来说,选择哪种容器取决于具体的需求:如果需要有序的元素集合,可以使用...

    Android清除缓存

    - **内存缓存**:Android应用中常见的内存缓存是使用`WeakHashMap`或者`LruCache`实现的内存对象缓存,它们存储在Dalvik/ART虚拟机的堆内存中。当内存不足时,系统会自动回收这些缓存。 - **外部存储缓存(外存)*...

    java中集合的用法与区别.docx

    Map接口则是用于存储键值对的集合,键(Key)必须是唯一的,而值(Value)可以重复。Map接口的实现类有Hashtable、HashMap、LinkedHashMap、WeakHashMap和IdentityHashMap。Hashtable与HashMap类似,但它是线程安全...

    set,list,map区别与联系.docx

    - **特点**:Map是一种键值对的数据结构,键(key)必须唯一,而值(value)可以重复。Map主要用于根据键来查找对应的值。 - **HashMap**:高效查找,基于哈希表,键和值都必须重写equals()和hashCode()。 - **...

    picketlink-jbas-common-2.6.0.CR4.zip

    例如,当缓存大量对象且不希望它们一直占用内存时,WeakIdentityHashMap可以确保一旦对象不再被使用,它们就会被自动清除。 【标签】"开源项目" 表明这些内容是开放源代码的,这意味着开发者可以自由地查看、使用、...

    多年的开发经验,与大家共享

    这意味着在多线程环境中,你可以直接使用`Hashtable`而不用担心数据同步问题。相反,`HashMap`实现了`Map`接口,它的方法默认是非同步的。在需要线程安全的情况下,可以使用`Collections.synchronizedMap(Map m)`...

    java集合总结.pdf

    WeakHashMap是Map接口的基于弱键的实现,以弱键实现的基于哈希表的Map。在WeakHashMap中,当某个键不再正常使用时,将自动移除其条目。 Java集合类提供了多种实现,用于解决不同的数据存储和操作问题。开发者可以...

Global site tag (gtag.js) - Google Analytics